revoke 必须严格匹配 grant 的粒度和用户标识,否则无效;撤销后旧连接权限仍有效,需重连或 set role none;验证须用 show grants,细粒度权限需分层显式清理。

直接用 REVOKE 语句,但必须严格匹配当初 GRANT 的粒度和用户标识,否则看似成功实则无效。
REVOKE 语法必须跟 GRANT 完全对齐
MySQL 不会自动推断你要撤哪一层权限。比如当初执行的是:
GRANT SELECT ON sales.orders TO 'report'@'192.168.1.%';
那撤销时也得写成:
REVOKE SELECT ON sales.orders FROM 'report'@'192.168.1.%';
若错写成 REVOKE SELECT ON sales.* 或 REVOKE SELECT ON *.*,就可能多撤或少撤,甚至报错。
-
'report'@'192.168.1.%'和'report'@'localhost'是两个完全独立的账号,主机名不一致会导致 “Unknown user” - 全局权限(如
SUPER、GRANT OPTION)只能在*.*上撤销,写成db.*会语法错误 -
ALL PRIVILEGES不包含GRANT OPTION,必须显式加在语句里:REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'u'@'h';
撤销后权限不立即在旧连接中生效
执行 REVOKE 成功只表示服务端权限表已更新,但已建立的客户端连接(包括应用连接池里的长连接)仍持有原权限快照。
- 用户还能继续执行被撤销的操作,直到重连或显式重置角色(MySQL 8.0+ 可用
SET ROLE NONE;) -
FLUSH PRIVILEGES在 MySQL 8.0+ 中通常不需要——REVOKE自带缓存刷新;它只在你手动改了mysql.user等系统表后才必需 - 验证是否真撤掉,别信 “Query OK”,要立刻运行:
SHOW GRANTS FOR 'u'@'h';,确认输出里已剔除对应权限行
细粒度权限容易残留,必须分层清理
撤销 SELECT ON db.* 不会清除之前单独授予的列级或表级权限,它们依然有效。
- 列级权限(如
SELECT(col_a))必须显式写出列名:REVOKE SELECT(col_a) ON db.tbl FROM 'u'@'h'; - 检查残留:查
mysql.columns_priv和mysql.tables_priv表,确认没有漏网之鱼 - 完整清理建议顺序:从细到粗 —— 先撤
ON db.tbl,再撤ON db.*,最后考虑ON *.* - 权限越多,每次 SQL 解析时权限检查越慢,生产环境应避免混用多层授权
真正难的不是写对那条 REVOKE,而是搞清目标用户到底有哪些权限、在哪一层被授予过、当前连接是否还在用旧权限——这些信息不查系统表、不重连验证,根本没法闭环。











