mysql_secure_installation默认清理test库、test_开头的库及User为空或Host为localhost且无密码的匿名账户,但不删除有用户名和密码的账号;执行后需FLUSH PRIVILEGES,MySQL 8.0+中部分功能受限或不可用。

mysql_secure_installation 脚本到底清理哪些东西
它默认会删掉 test 库、所有以 test_ 开头的库,清空 mysql.user 表里 User 为空(即匿名用户)或 Host 为 localhost 但无密码的账户。但注意:它**不会**动你手动创建的非空用户名 + 密码的账号,哪怕 Host 是 % 或 127.0.0.1。
常见误判点:mysql_secure_installation 运行时问 “Remove anonymous users?”,如果你选 y,它只删 User = '' 的行;而像 root@localhost 这种即使没设密码,也不会被删——除非你之前手动执行过 DROP USER ''@'localhost' 或类似操作。
为什么有时候运行完还有匿名用户残留
脚本只检查 mysql.user 表中 User = '' 的记录,但 MySQL 8.0+ 引入了角色(roles)和更细粒度的权限模型,有些匿名行为可能来自 mysql.proxies_priv 或未刷新的权限缓存。另外,如果你用的是容器或云数据库(如阿里云 RDS、AWS RDS),mysql_secure_installation 可能根本不可用——它们通常禁用了 root 远程登录,也不允许删系统库。
- 执行前先确认:运行
SELECT User, Host FROM mysql.user WHERE User = '';看是否真有空用户名 - 执行后务必运行
FLUSH PRIVILEGES;,否则删了也无效 - 某些发行版(如 Ubuntu 22.04 自带的 MySQL 8.0.32)在安装时已预删 test 库,脚本再跑一遍会提示 “The 'test' database does not exist”
手动清理比脚本更可控的几个关键操作
脚本是“一键式”,但生产环境建议分步验证。尤其要注意 Host 字段匹配逻辑——MySQL 认为 'localhost' 和 '127.0.0.1' 是两个不同 host,且 '%' 不匹配 localhost(这是 Unix socket 特性)。
安全清理三步:
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
- 删匿名用户:
DROP USER ''@'localhost'; DROP USER ''@'127.0.0.1';(如果存在) - 删测试库:
DROP DATABASE IF EXISTS test;,再查SHOW DATABASES LIKE 'test%';确保没有test_foo类库 - 检查冗余账号:
SELECT User, Host FROM mysql.user WHERE User NOT IN ('root', 'mysql.infoschema', 'mysql.session', 'mysql.sys') AND Host NOT IN ('localhost');(MySQL 8.0+ 系统账号名固定,可据此过滤)
mysql_secure_installation 在 MySQL 8.0+ 的兼容性问题
MySQL 官方从 8.0.29 开始将 mysql_secure_installation 改为“仅适用于全新初始化实例”,如果你是在已有数据的实例上运行,它可能跳过部分步骤,甚至报错 ERROR 1396 (HY000): Operation DROP USER failed——因为试图删一个根本不存在的空用户。
此时不要硬跑脚本。替代方案:
- 用
mysqld --initialize-insecure初始化新实例再迁移(不推荐线上) - 直接进 MySQL 执行上述手动语句,配合
SELECT plugin FROM mysql.user WHERE User = 'root';确认认证插件是否为caching_sha2_password,避免删完连不上 - 若用 Docker,可在
my.cnf中加skip-grant-tables临时绕过权限系统(仅限调试,切勿放生产)
真正容易被忽略的是:删完匿名用户后,如果应用连接字符串里写的是 user=(空值),它会 fallback 到匿名用户逻辑——这时候不是删没删干净,而是客户端还在主动“召唤”它。










