必须用drop user删除用户,它会自动清理mysql.user、db、tables_priv等全部权限表,避免delete from mysql.user导致的权限残留、缓存不一致及error 1396等故障。

必须用 DROP USER,别碰 DELETE FROM mysql.user
直接删表只会留下权限“孤儿”:mysql.db、mysql.tables_priv 里的记录全还在,后续重建同名用户可能意外继承旧库/表权限;更糟的是,MySQL 8.0+ 权限缓存不一致会导致 CREATE USER 报错 ERROR 1396,或删完还能连上。只有 DROP USER 是 MySQL 内部封装的 DDL 操作,它会自动同步清理 mysql.user、mysql.db、mysql.tables_priv、mysql.proxies_priv 等全部权限表,无需手动 FLUSH PRIVILEGES。
删之前必须查清三类隐性依赖:连接、代理、角色
用户删了,但应用还在用老连接跑,或靠它代理登录,或通过角色继承权限——这些都会导致服务中断或权限残留。执行前务必确认:
- 活跃连接:
SELECT ID, USER, HOST, COMMAND, TIME FROM information_schema.PROCESSLIST WHERE USER = 'xxx',有结果就得先KILL掉 - 代理关系:
SELECT * FROM mysql.proxies_priv WHERE User = 'xxx' AND Host = 'yyy',若存在,说明其他账号靠它做 PROXY 登录,删了会连锁失败 - 角色链路:
SELECT FROM_USER, TO_USER, WITH_ADMIN_OPTION FROM mysql.role_edges WHERE FROM_USER = 'xxx'(该用户授过哪些角色)、WHERE TO_USER = 'xxx'(谁把角色给了它)、WHERE TO_ROLE = 'role_name'(若要删角色本身,得先确认是否被引用)
MySQL 8.0+ 的角色残留必须手动清理
DROP USER 会解绑用户直授的角色,但不会清理它曾以 WITH ADMIN OPTION 授出的角色边(orphaned edge)。现象是:删完 'alice'@'%' 后,SELECT * FROM mysql.role_edges WHERE FROM_USER = 'alice' 仍有记录。这不是 bug,是设计行为。必须手动执行:
DELETE FROM mysql.role_edges WHERE FROM_USER = 'alice' AND FROM_HOST = '%'; FLUSH PRIVILEGES;
注意:FLUSH PRIVILEGES 不可省——内存缓存不更新,SHOW GRANTS 仍会显示旧关系;DROP USER IF EXISTS 对这类残留完全无感,它只跳过“用户不存在”的报错。
查僵尸账号不能只看 mysql.user,得用 performance_schema.accounts
mysql.user 里没有“最后登录时间”,password_last_changed 只反映改密动作。真正能判断是否闲置的是 performance_schema.accounts,但它默认关闭:
- 先开消费者:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'accounts' - 查 90 天未登录的非系统账号:
SELECT USER, HOST FROM performance_schema.accounts WHERE USER NOT IN ('mysql.session', 'mysql.sys', 'root') AND LAST_SEEN - 补充筛“半死”账号(锁住 + 无权限 + 认证失效):
SELECT User, Host FROM mysql.user WHERE account_locked = 'Y' AND Select_priv = 'N' AND authentication_string REGEXP '^\*0$|^\*1$|^$'
新创建但从未登录的账号不会出现在 accounts 表里——它压根没被认证系统“看见”过,这点容易漏。











