必须用DROP USER原子清理所有权限表,它自动同步清除mysql.user、mysql.db、mysql.role_edges等记录并刷新内存缓存,无需FLUSH PRIVILEGES;手动DELETE FROM mysql.user仅删主表,会导致悬空权限、旧连接持续生效及重建同名用户时意外继承权限。

直接用 DROP USER,别碰 mysql.user 表
手动 DELETE FROM mysql.user 是最常见也最危险的误操作。它只删主表,mysql.db、mysql.tables_priv、mysql.role_edges 里的记录全留着,形成悬空权限——新连接登不上,但旧连接还能继续执行语句,审计工具也会误判。
DROP USER 'u'@'h' 是原子操作:MySQL 内部会同步清空所有权限表对应行,且自动重载内存缓存,不需要再执行 FLUSH PRIVILEGES。加 IF EXISTS 可避免 ERROR 1396 报错,适合脚本化清理。
- 必须写全
'u'@'h','u'@'localhost'和'u'@'%'是两个独立账号 - 用户名区分大小写,主机名不区分;通配符如
'192.168.%'要原样拼写 - 若之前手删过表,现在执行
DROP USER会自动覆盖修复不一致状态
删完立刻查 mysql.db 和 mysql.role_edges
哪怕用了 DROP USER,也要验证是否真清零。残留最常出现在这两处:
执行:SELECT * FROM mysql.db WHERE User = 'u' AND Host = 'h';SELECT * FROM mysql.role_edges WHERE FROM_USER = 'u' AND FROM_HOST = 'h';
任一有返回,就说明存在脏数据。尤其注意 Host = ''(空字符串)的情况,老版本升级到 8.0 后容易遗留。
-
mysql.db有残留 → 权限还在,只是登录失败,重建同名用户会意外继承 -
mysql.role_edges有孤立边 → 不影响运行,但审计、同步工具或权限图谱会误判逻辑关系 - 确认有残留时,才手动
DELETE FROM mysql.role_edges WHERE ...,之后必须FLUSH PRIVILEGES
活连接不断开,权限还在生效
DROP USER 不杀已有连接。应用连接池里的长连接仍维持旧身份和权限,直到超时或主动断开。这期间可能产生越权操作或数据污染。
查活跃会话:SELECT ID, USER, HOST, COMMAND, TIME FROM information_schema.PROCESSLIST WHERE USER = 'u';
- 需要立即阻断时,用
KILL 1234;(ID 替换为上一步查到的值),不是KILL CONNECTION - 代理账号(
GRANT PROXY ON 'u'@'%' TO 'p'@'%')不受影响,但被代理方删掉后,代理登录会静默失败 - 真正安全的做法是:先停应用侧连接池配置,等所有连接自然释放后再删用户
数据库对象不会被连带删除,得单独处理
DROP USER 只管权限元数据,完全不碰用户创建的库、表、视图、函数等。比如 app@'%' 建了库 app_data,删用户后该库原封不动。
若需一并清理:SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'app_data';DROP DATABASE IF EXISTS app_data;
- 删库后,其他账号对它的权限(如
GRANT SELECT ON app_data.* TO 'r'@'%')会变成ON non_existent_db.*,虽不报错但建议同步清理 - 不要假设“同名库一定归属该用户”,先查
information_schema.SCHEMATA确认归属 - 配置文件里硬编码的用户名(JDBC URL、ProxySQL 的
mysql_users表、主从SOURCE_USER)也不会自动失效
真正的清理难点不在命令本身,而在于你是否记得检查连接池配置、角色绑定链路,以及那些藏在 Host = '' 或 TRIM(User) = '' 里的隐形账号。


















