需查mysql.role_edges表三类关系:FROM_USER确认用户授出角色、TO_USER确认谁授予该用户、TO_ROLE确认角色被哪些用户引用;残留需手动DELETE并FLUSH PRIVILEGES。

MySQL 8.0+ 中如何查清用户与角色的绑定关系
角色(ROLE)是 MySQL 8.0 引入的权限抽象层,一个用户可能没直授任何权限,却通过 GRANT role_name TO 'u'@'h' 继承全部能力。删用户时若不检查角色链路,会留下孤立边(orphaned edge),即 mysql.role_edges 表中 FROM_USER 已不存在,但 TO_USER 或 TO_ROLE 还在,形成逻辑脏数据。
必须查三类关系:
-
SELECT * FROM mysql.role_edges WHERE FROM_USER = 'xxx' AND FROM_HOST = 'yyy';—— 查该用户授予了哪些角色,或被哪些角色授予 -
SELECT * FROM mysql.role_edges WHERE TO_USER = 'xxx' AND TO_HOST = 'yyy';—— 查谁把角色授给了这个用户(含代理链) -
SELECT * FROM mysql.role_edges WHERE TO_ROLE = 'xxx';—— 若要删角色本身,得确认它是否还被其他用户引用
特别注意:MySQL 8.0.16+ 支持 DROP ROLE IF EXISTS role1;,但角色删除**不影响已登录用户的会话权限**,只影响新连接;而用户删除时,DROP USER 会自动解绑其角色(8.0+ 行为),5.7 不会。
为什么 DROP USER 后仍发现角色残留?
常见现象是执行 DROP USER 'alice'@'%' 后,再查 mysql.role_edges 还有记录,比如 FROM_USER='alice' 的行未被清理。这不是 bug,而是因为该用户曾被 GRANT ... WITH ADMIN OPTION 授予过角色管理权,导致其作为“管理员”身份被写入角色边表,而 MySQL 仅在用户主记录删除时清理直接绑定,不递归清理其曾授予他人的角色授权链。
真正需要清理的是这类“悬空管理员”残留:
SELECT * FROM mysql.role_edges WHERE FROM_USER = 'alice' AND FROM_HOST = '%';- 若返回非空,说明该用户曾以管理员身份参与角色分发,必须手动清理:
DELETE FROM mysql.role_edges WHERE FROM_USER = 'alice' AND FROM_HOST = '%'; - 清理后必须执行
FLUSH PRIVILEGES;,否则内存缓存不更新,后续SHOW GRANTS可能仍显示旧关系
别指望 DROP USER IF EXISTS 自动兜底——它只是跳过不存在用户的报错,对已删用户的残留记录完全无感。
如何安全清理不再使用的角色本身
角色不是用户,不能用 DROP USER 删。必须用 DROP ROLE,但它不检查依赖:如果某角色已被授予给多个用户,DROP ROLE 会直接失败(ERROR 3715),除非先 REVOKE。
正确流程是:
- 先查谁用了这个角色:
SELECT FROM_USER, FROM_HOST FROM mysql.role_edges WHERE TO_ROLE = 'app_reader'; - 逐个回收:
REVOKE app_reader FROM 'dev1'@'%'; REVOKE app_reader FROM 'dev2'@'10.%.%.%'; - 确认无引用后执行:
DROP ROLE app_reader; - 验证清零:
SELECT * FROM mysql.role_edges WHERE TO_ROLE = 'app_reader';应为空
注意:DROP ROLE 不影响当前已建立的连接——那些会话仍保有该角色赋予的权限,直到断开重连。如需立即生效,需配合 KILL 相关连接或重启应用连接池。
清理前最容易被忽略的两个检查点
一是 mysql.proxies_priv 表里的代理关系。比如 GRANT PROXY ON 'target'@'%' TO 'proxy'@'%',删掉 target 用户会导致 proxy 登录直接失败,且错误提示不明确(Access denied),排查成本高。
二是复制配置里硬编码的账号。例如主从复制中 CHANGE REPLICATION SOURCE TO SOURCE_USER='repl_user',这个账号即使没业务连接,也不能删——删了从库就断连。
所以清理前必须跑这两条:
SELECT * FROM mysql.proxies_priv WHERE CONCAT(User, '@', Host) = 'target@%';-
SELECT * FROM performance_schema.replication_connection_configuration WHERE SOURCE_USER = 'repl_user';(MySQL 8.0+)或查SHOW SLAVE STATUS\G中的Master_User
真正麻烦的从来不是命令怎么写,而是你不知道哪个地方悄悄写着这个用户名——它不在 mysql.user 里,却在某个配置文件、某个 SQL 文件、某个 ProxySQL 的 mysql_users 表里。


















