mysql撤销权限必须严格匹配原grant语句,包括数据库名、用户主机名、权限类型及grant option;细粒度权限需单独处理,撤销后需重连验证,残留权限须查mysql.tables_priv等系统表。

REVOKE ON db_name.* 必须和当初 GRANT 完全一致
撤销特定数据库的所有访问权限,不能靠“猜”或“大概匹配”。MySQL 不会自动推导你指的是哪个库、哪个用户。比如当初执行的是:GRANT SELECT, INSERT ON app_log.* TO 'logger'@'10.0.2.%',那撤销时就必须写:REVOKE SELECT, INSERT ON app_log.* FROM 'logger'@'10.0.2.%'。漏掉 INSERT,就只撤了读权限;主机名写成 '%' 而不是 '10.0.2.%',命令会成功但实际无效——因为那是另一个账号。
-
app_log.*和app_log.t1是不同粒度,不能混用 - 权限名大小写敏感:
SELECT有效,select报错 - 如果当初授权用了
WITH GRANT OPTION,它不会被自动撤掉,必须显式加GRANT OPTION到 REVOKE 语句里
别用 ALL PRIVILEGES ON *.* 来“清空”单个库权限
想撤掉某个用户对 report_db 的所有权限,写 REVOKE ALL PRIVILEGES ON report_db.* FROM 'analyst'@'%' 是对的;但若误写成 REVOKE ALL PRIVILEGES ON *.* FROM 'analyst'@'%',就会把该用户在所有库上的权限全干掉——包括可能还依赖的 mysql 或 performance_schema 系统库权限,导致后续管理异常。
-
ALL PRIVILEGES不包含GRANT OPTION,要一起撤得写成:REVOKE ALL PRIVILEGES, GRANT OPTION ON report_db.* FROM 'analyst'@'%' - 如果用户还通过角色继承了权限(MySQL 8.0+),
REVOKE只影响直接授予的部分,需额外执行REVOKE role_name FROM 'analyst'@'%' - 列级或表级单独授过的权限(如
SELECT(col_a) ON report_db.users)不会被ON report_db.*撤掉,得单独处理
撤销后旧连接仍保留原权限
执行 REVOKE 成功返回 Query OK,不代表应用立刻失去访问能力。已建立的连接(尤其是连接池里的长连接)仍持有授权快照,SELECT 还能跑,INSERT 还能写,直到连接断开重连。
- 验证是否真生效,别信返回结果:立刻运行
SHOW GRANTS FOR 'analyst'@'%',确认输出里没了report_db.*相关行 - 生产环境建议配合应用重启或连接池强制刷新,否则权限变更形同虚设
-
FLUSH PRIVILEGES在 MySQL 8.0+ 中非必需,但加了不报错;真正关键的是让客户端重连
残留权限常藏在 mysql.tables_priv 或 mysql.columns_priv 里
你以为撤干净了?查 mysql.user 和 mysql.db 表可能显示为空,但细粒度权限往往落在其他系统表里。比如曾给 'analyst'@'%' 单独授过 UPDATE(name) 权限,它就存在 mysql.columns_priv 中,ON report_db.* 的撤销完全不影响它。
- 检查残留:运行
SELECT * FROM mysql.tables_priv WHERE User='analyst' AND Host='%'和SELECT * FROM mysql.columns_priv WHERE User='analyst' AND Host='%' - 清除列级权限必须显式写出列名:
REVOKE UPDATE(name) ON report_db.users FROM 'analyst'@'%' - 清理顺序建议从细到粗:先撤表/列级,再撤库级,最后考虑全局——避免遗漏,也防止误扩范围
真正麻烦的不是写错那条 REVOKE,而是你根本不知道这个用户到底在哪几层、通过哪几种方式获得了哪些权限。不查系统表、不重连验证、不看 SHOW GRANTS 输出,就等于没撤。











