必须用 drop user 删除用户,delete from mysql.user 会导致权限残留和缓存不一致;查僵尸账号应使用 performance_schema.accounts 表,需先启用 accounts 消费者,再查询 last_seen 超过90天的非系统账号。

必须用 DROP USER 删除,DELETE FROM mysql.user 会留下权限残留、触发缓存不一致,甚至导致后续 CREATE USER 失败。
查僵尸账号不能只看 mysql.user,得结合 performance_schema.accounts
MySQL 不记录“最后登录时间”,mysql.user 里的 password_last_changed 只反映改密动作,不是登录行为。真正能查活跃度的是 performance_schema.accounts 表,但它依赖功能开启:
- 先确认是否启用:
SELECT VARIABLE_VALUE FROM performance_schema.setup_consumers WHERE NAME = 'accounts';,返回NO就得手动开: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 - 注意:
LAST_SEEN是最后一次成功认证时间;新创建但从未登录的账号不会出现在这张表里——它压根没被“看见”过
锁定但未清理的账号要单独筛出来
有些账号被 ALTER USER ... ACCOUNT LOCK 或 REVOKE 后就扔着不管了,它们在 mysql.user 里还留着,但已无法登录。这类“半死”账号满足三个条件:
account_locked = 'Y'- 所有基础权限字段(
Select_priv,Insert_priv,Update_priv等)全为'N' -
authentication_string为空、或以*0/*1开头(无效哈希占位符)
执行这句就能揪出来:SELECT User, Host FROM mysql.user WHERE account_locked = 'Y' AND Select_priv = 'N' AND Insert_priv = 'N' AND Update_priv = 'N' AND Delete_priv = 'N' AND Create_priv = 'N' AND Drop_priv = 'N' AND authentication_string REGEXP '^\*0$|^\*1$|^$';
DROP USER 前必须检查三件事:连接、代理、角色
删完就断服务?大概率是没查清隐性依赖。重点盯这三处:
- 查活跃连接:
SELECT ID, USER, HOST, COMMAND, TIME FROM information_schema.PROCESSLIST WHERE USER = 'xxx';,有结果就得先KILL掉 - 查代理关系:
SELECT * FROM mysql.proxies_priv WHERE CONCAT(User, '@', Host) = 'xxx@%';,如果其他账号靠它代理登录,删了就会连锁报错 - 查角色绑定:
SELECT FROM_USER, TO_USER, WITH_ADMIN_OPTION FROM mysql.role_edges WHERE FROM_USER = 'xxx';,MySQL 8.0+ 虽会自动解绑,但若角色含WITH ADMIN OPTION,重建同名用户时可能意外继承权限
别跳过这步——很多“删完还能连上”的问题,根源都在 proxies_priv 或连接池硬编码里。
删完不是终点,残留权限和配置引用才是真坑
DROP USER 在 MySQL 5.7+ 是原子操作,会清掉 mysql.user、mysql.db、mysql.tables_priv 等所有关联表,但有两个例外:
- 它不碰应用侧配置:JDBC URL、Druid/Hikari 连接池、备份脚本、ProxySQL 的
mysql_users表、主从复制的SOURCE_USER—— 这些都得人工 grep 或进对应系统查 - 它不删该用户创建的对象:数据库、表、函数、事件等,这些得单独处理,否则磁盘空间照占
最常被忽略的,是删完后没验证 SHOW GRANTS FOR 'xxx'@'yyy'; 是否真返回空——如果还显示权限,说明有残留或缓存没刷,这时候再跑一遍 FLUSH PRIVILEGES; 已无意义,得回头查 mysql.db 表有没有漏删的行。











