必须用 drop user 删除用户,它自动清理 mysql.user、mysql.db、mysql.tables_priv 等所有权限表并刷新内存缓存;直接 delete from mysql.user 仅删主表,导致权限残留、缓存不一致及重建失败。

必须用 DROP USER,别碰 mysql.user 表
直接 DELETE FROM mysql.user 是最常见也最危险的操作——它只删主表,mysql.db、mysql.tables_priv、mysql.proxies_priv 等权限表里的记录全留着。MySQL 8.0+ 还有内存级权限缓存,删完不 FLUSH PRIVILEGES 可能连不上;即使 flush 了,也可能触发 ERROR 1396 (HY000): Operation CREATE USER failed for 'xxx'@'%',系统认为用户“还存在”,但查 mysql.user 又找不到。
DROP USER 是唯一安全方式:它自动清理所有关联权限表,包括角色绑定(8.0+)、代理关系、数据库级权限等,且内部自动重载权限缓存,无需手动 FLUSH PRIVILEGES。
- MySQL 8.0 要求必须写全
'user'@'host',DROP USER 'test'会报错 - 5.7 允许省略 host,默认匹配
'test'@'%',但跨版本脚本必须显式声明 host - 批量删除用逗号分隔:
DROP USER 'u1'@'localhost', 'u2'@'%'; - 防误删加
IF EXISTS:DROP USER IF EXISTS 'olduser'@'192.168.%';
删前必须确认账号没在连接中
DROP USER 不会中断当前活跃连接,旧会话仍维持原权限直到断开。若需立即阻断,得先查再杀:
- 查活跃连接:
SELECT ID, USER, HOST, COMMAND, TIME FROM information_schema.PROCESSLIST WHERE USER = 'olduser'; - 生成 KILL 语句:
SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE user = 'olduser'; - 注意:用
KILL 1234;,不是KILL CONNECTION 1234;(后者是 5.7+ 新语法,兼容性差)
更隐蔽的风险是代理关系:SELECT * FROM mysql.proxies_priv WHERE CONCAT(User, '@', Host) = 'olduser@%';。删掉被代理的账号后,代理账号登录会静默失败,错误信息里不提示原因。
别只看 user 字段,host 必须配对清理
MySQL 用户由 'username'@'host' 共同唯一标识。'test'@'localhost' 和 'test'@'%' 是两个完全独立的账号。漏删任意一个,都可能让残留权限继续生效。
执行前务必查清全部实例:
SELECT User, Host FROM mysql.user WHERE User = 'test';-
SHOW GRANTS FOR 'test'@'localhost';和SHOW GRANTS FOR 'test'@'%';分别确认权限范围 - 注意大小写:用户名区分大小写(取决于
lower_case_table_names),主机名不区分;通配符如'192.168.%'必须原样拼写
特别警惕空用户:SELECT Host, User FROM mysql.user WHERE TRIM(User) = '';。这类账号常搭配 Host = '%',意味着任意主机都能连(尤其空密码时),是严重安全隐患。
删完还得检查配置文件和下游依赖
账号删了,但应用可能还在硬编码使用它。最常见的三个盲区是:
- MySQL 主从复制的
CHANGE REPLICATION SOURCE TO(旧版CHANGE MASTER TO)里的SOURCE_USER - ProxySQL 的
mysql_users表(查SELECT username FROM mysql_users WHERE username = 'olduser';) - 应用配置:JDBC URL、Spring Boot 的
spring.datasource.username、定时备份脚本中的-u参数
搜代码库时别只搜用户名,还要搜 @ 符号——比如 test@% 或 test@localhost 可能被写死在连接字符串里。删完立刻验证:SELECT User, Host FROM mysql.user WHERE User = 'olduser'; 应返回空集;SHOW GRANTS FOR 'olduser'@'%'; 应报错 ERROR 1141 (42000)。
真正麻烦的从来不是怎么删,而是删完才发现某处 cron 脚本连不上、监控告警失灵、或者 ProxySQL 后端直接断流——这些依赖关系往往藏得深,且不会在 mysql.user 里留下任何痕迹。











