MySQL不支持REVOKE ALL FROM role_name,必须显式指定DML权限名和作用域(如REVOKE INSERT ON sales.* FROM 'app_writer'@'%'),撤销后不影响已绑定该角色的用户权限,需解绑或DROP ROLE才能使用户立即失效。

撤销角色的DML权限必须显式指定权限名和作用域
MySQL 不允许对角色执行 REVOKE ALL FROM role_name,这种写法直接报错 ERROR 1064 (42000)。DML 权限(如 SELECT、INSERT、UPDATE、DELETE)属于静态权限,必须逐条、精确地从角色定义中移除,且 ON 子句必须与当初 GRANT 时完全一致。
- 先确认角色当前有哪些 DML 权限:
SHOW GRANTS FOR 'app_writer'@'%';(注意:角色名要加引号,且主机名用'%'或具体值,不能省略) - 若当初是
GRANT INSERT, UPDATE ON sales.* TO 'app_writer'@'%';,则必须写:REVOKE INSERT, UPDATE ON sales.* FROM 'app_writer'@'%'; - 不能写成
REVOKE INSERT ON *.*或REVOKE INSERT ON sales——前者范围过大,后者语法错误 - 表级或列级权限需单独处理:
REVOKE SELECT(col_a) ON sales.orders FROM 'app_writer'@'%';
撤销后不会影响已绑定该角色的用户
这是最容易误判的一点:角色只是权限容器,撤销角色内的 UPDATE 并不自动让已绑该角色的用户失去 UPDATE 能力。他们的权限快照在连接建立时已加载,除非断开重连或执行 SET ROLE NONE; SET ROLE 'app_writer'; 才会重新评估。
- 真正让现有用户“立即失效”的操作只有两个:
REVOKE 'app_writer' FROM 'user'@'host';(解绑),或DROP ROLE 'app_writer';(删容器) - 如果目标是清理历史授权但保留角色结构,只撤角色内权限即可;如果想彻底停用,优先
REVOKE ... FROM用户,再DROP ROLE -
DROP ROLE会失败若仍有用户绑定它,错误是ERROR 3530 (HY000),必须先解绑
别混淆角色权限和动态权限
像 SELECT 这类 DML 权限是静态权限,走 mysql.tables_priv 等系统表;而 BACKUP_ADMIN、SYSTEM_VARIABLES_ADMIN 是动态权限,语法完全不同(需 REVOKE SYSTEM PRIVILEGE ...)。混用会导致语法错误或静默无效。
- 查角色是否还带动态权限:
SHOW GRANTS FOR 'app_writer'@'%';输出里若出现GRANT BACKUP_ADMIN ON *.*,那是另一套机制,不能用普通REVOKE SELECT去碰 - 动态权限只能作用于
*.*,不支持db.*或db.tbl,也**不能**用于 DML 控制 - 角色本身不继承动态权限——动态权限是直接授给用户的,角色里只存静态权限
验证是否真撤掉,不能只看 Query OK
执行 REVOKE 成功返回 Query OK,不代表权限已从角色定义中清除。MySQL 8.0+ 的权限缓存较敏感,最可靠的方式是立刻查授权记录。
- 运行:
SHOW GRANTS FOR 'app_writer'@'%';,确认输出中不再含目标 DML 行(如不再有GRANT INSERT ON sales.*) - 如果仍存在,说明
ON子句不匹配,或当初授权用了不同主机名('app_writer'@'localhost'和'app_writer'@'%'是两个角色) - 极端情况下可查底层:
SELECT * FROM mysql.role_edges WHERE FROM_USER = 'app_writer';确认角色没被意外复用
角色的 DML 权限撤销动作本身很轻量,但它的效果边界容易被高估——它只改容器,不触用户,也不刷连接。生产环境做权限回收,关键不是“有没有执行 REVOKE”,而是“谁还在用它”和“连接是否已刷新”。











