直接DELETE FROM mysql.user WHERE Host = '%'会残留mysql.db等表中的权限记录,导致权限错乱且缓存不一致;应先识别高危账号,再用DROP USER安全删除并验证清理效果。

为什么DELETE FROM mysql.user WHERE Host = '%'是危险操作
直接删表会留下大量孤儿权限记录。比如某个账号 'app'@'%' 曾被授予 GRANT SELECT ON app_db.*,那这条权限实际存在 mysql.db 表里;只删 mysql.user 后,mysql.db 里仍保留着 Host = '%' 的行,后续新建同名用户可能触发冲突或权限错乱。更麻烦的是,MySQL 8.0+ 权限缓存机制会让这种不一致持续生效,直到重启或手动 FLUSH PRIVILEGES ——但后者不一定能修复结构损坏。
先识别哪些 Host = '%' 账号真该删
不是所有 Host = '%' 都要砍。有些是运维跳板(如 'dba'@'%'),有些是应用连接池必需(如 'app_rw'@'%')。重点盯三类:
-
Super_priv = 'Y'或Grant_priv = 'Y'且Host = '%' -
User为空或仅含空白字符(即匿名账号)且Host = '%' -
account_locked = 'Y'且password_last_changed超过 180 天,同时无任何last_seen记录(需performance_schema.accounts开启)
查高危组合的语句:SELECT User, Host, Super_priv, Grant_priv FROM mysql.user WHERE Host = '%' AND (Super_priv = 'Y' OR Grant_priv = 'Y');
安全删除必须用 DROP USER,不能 DELETE
DROP USER 是唯一能自动清理全链路权限的命令。它会同步删掉 mysql.db、mysql.tables_priv、mysql.proxies_priv 等所有关联条目。而 DELETE FROM mysql.user 只动一张表,后面还得手动清理五张表、再 FLUSH PRIVILEGES,稍有遗漏就埋雷。
- 单个删:
DROP USER 'risk_user'@'%'; - 批量生成语句(复制结果后执行):
SELECT CONCAT('DROP USER ''', User,'''@''%', '''') AS stmt FROM mysql.user WHERE Host = '%' AND (Super_priv = 'Y' OR TRIM(User) = ''); - 加
IF EXISTS防报错:DROP USER IF EXISTS 'old_dev'@'%';
删完必须验证是否残留 + 收缩替代方案
删完别立刻关终端。立即跑一遍确认:
SELECT User, Host FROM mysql.user WHERE Host = '%'; —— 应返回空集
SELECT Host, Db FROM mysql.db WHERE Host = '%'; —— 同样应为空
如果仍有残留,说明之前有人用 DELETE 手改过表,此时优先考虑重建权限表或升级 MySQL 版本修复内部状态。另外,对必须保留 Host = '%' 的账号,至少把密码强度拉满、绑定 IP 白名单(如用 CREATE USER 'app'@'%' REQUIRE SUBJECT '/CN=app-server' 做 TLS 校验),或者干脆换成更细粒度的 'app'@'10.20.%'。


















