<p>REVOKE SHUTDOWN ON . 是唯一合法语法,因SHUTDOWN是全局权限,必须作用于整个实例,作用域只能为.,且需FLUSH PRIVILEGES生效并主动KILL旧连接。</p>

REVOKE SHUTDOWN ON *.* 是唯一合法语法
SHUTDOWN 是全局权限,只能作用于整个实例,不能按库、表或列粒度回收。MySQL 强制要求作用域必须是 *.*,写成 mydb.*、* 或漏掉 ON 都会报错:ERROR 1221 (HY000): Incorrect usage of DB GRANT and GLOBAL PRIVILEGES。
正确写法只有一种:
REVOKE SHUTDOWN ON *.* FROM 'appuser'@'192.168.1.%';
- 主机名必须完全匹配——
'appuser'@'%'和'appuser'@'localhost'是两个用户,得分别回收 - 如果用户通过 DNS 解析连接(如
'appuser'@'web01.internal'),也要用对应 host 回收 - 通配符
%不覆盖localhost,这是 MySQL 的硬性规则,不是配置问题
MySQL 8.0+ 仍支持 SHUTDOWN,但需 FLUSH PRIVILEGES 显式生效
虽然 SUPER 在 8.0.16+ 被废弃,但 SHUTDOWN 权限仍在,字段名仍是 Shutdown_priv,底层仍通过 mysql.user 表存储。不过 REVOKE 命令本身不会自动刷新所有缓存场景,尤其跨版本或权限异常时,建议执行:
FLUSH PRIVILEGES;
- 这步不是“让 REVOKE 生效”的开关,而是强制重载整张权限表,避免残留
- 不执行它,旧连接可能继续持有该权限;新连接一般能立即识别 REVOKE 结果
- 高并发实例中,
FLUSH PRIVILEGES可能短暂锁表,避开业务高峰执行
必须检查 mysql.user 表里是否还有 Shutdown_priv='Y'
SHOW GRANTS FOR 'appuser'@'%' 可能不显示 SHUTDOWN,因为它是全局隐式权限,不参与 GRANT 输出逻辑。真正权威的判断依据是 mysql.user 表:
SELECT Host, User, Shutdown_priv FROM mysql.user WHERE Shutdown_priv = 'Y';
- 结果中出现非
root用户(如monitor、backup)就说明没清干净 - 某些迁移脚本或初始化容器可能直接
INSERT INTO mysql.user,绕过 GRANT/REVOKE 流程 - 发现残留时,不要手改
Shutdown_priv字段——MySQL 运行时只认内存加载的权限,必须用REVOKE触发变更
回收后旧连接仍能执行 SHUTDOWN,必须主动 KILL
REVOKE 只影响新建立的连接。已存在的活跃会话(包括应用长连接、DBA 客户端)仍可执行 SHUTDOWN,哪怕你刚执行完 REVOKE。
- 查活跃会话:
SELECT ID, USER, HOST, COMMAND, TIME FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'appuser'; - 对可疑连接执行:
KILL [ID];(注意:需要CONNECTION_ADMIN或等效权限) - 监控类工具(如 Zabbix 自定义脚本)若申请了
SHUTDOWN来“探活”,属于误用——应改用SELECT 1或 MySQL 协议 PING
权限回收最易被忽略的点,其实是连接生命周期管理:你以为权限没了,其实老连接还在拿着钥匙开门。


















