Troubleshooting MySQL crash when FLUSH PRIVILEGES

近期在客户执行 MySQL 用户创建及授权操作时,数据库实例发生异常。经排查确认,原因为 MySQL 权限表损坏,导致权限变更操作触发实例异常。

考虑到数据库规模约 80GB,为保证恢复效率与稳定性,采用业务数据逻辑导出、新建数据库并重建用户权限后再导入数据的方式完成恢复。数据库运行正常业务恢复。

问题现象:

MySQL数据库在做创建用户并授权,执行FLUSH PRIVILEGES时,数据库CRASH, 后面反复重启未能启动。

Note: 从函数调用中有看到是用户访问权限表。

关键函数调用链

  1. acl_init() – 权限表初始化
  2. acl_reload() – 权限表重新加载
  3. reload_priv_idx() – 权限索引重新加载
  4. populate_roles_caches() – 角色缓存填充
  5. find_acl_user() – 查找ACL用户

MySQL权限表架构解析

MySQL权限系统采用多级缓存机制:

  1. 内存缓存: 权限信息加载到内存中
  2. 磁盘存储: mysql数据库中的权限表
  3. 缓存同步: 通过 FLUSH PRIVILEGES 同步
// 简化的权限加载流程
acl_init() 
    → acl_reload()
        → reload_priv_idx()
            → populate_roles_caches()

可能的原因分类:

类别具体可能性优先级
数据文件问题1. 权限表(mysql.user)损坏或格式不兼容
2. 数据文件物理损坏
软件配置问题1. MySQL二进制文件或DLL损坏
2. 配置文件错误
3. 版本兼容性问题
系统环境问题1. 内存硬件故障
2. Windows权限不足
3. 文件系统权限问题
操作问题1. 不当的权限表操作
2. 未授权的数据修改

RENAME USER或create user, FLUSH PRIVILEGES 操作需要重新加载权限表, 重新加载过程中访问了无效或已释放的内存地址.

解决方法:

方案一:安全模式启动修复(推荐)

适用场景: 权限表轻微损坏,需要保留现有数据

# 1. 停止MySQL服务
net stop MySQL80

# 2. 以跳过权限检查模式启动
C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld --console --skip-grant-tables

# 3. 连接MySQL(无需密码)
mysql -u root

# 4. 修复权限表
FLUSH PRIVILEGES;

# 5. 重启正常服务

方案二:权限表替换

mysql> select * from mysql.tables_priv;

mysql> show create table mysql.tables_priv\G
*************************** 1. row ***************************
       Table: tables_priv
Create Table: CREATE TABLE `tables_priv` (
  `Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
  `Db` char(64) COLLATE utf8mb3_bin NOT NULL DEFAULT '',
  `User` char(32) COLLATE utf8mb3_bin NOT NULL DEFAULT '',
  `Table_name` char(64) COLLATE utf8mb3_bin NOT NULL DEFAULT '',
  `Grantor` varchar(288) COLLATE utf8mb3_bin NOT NULL DEFAULT '',
  `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `Table_priv` set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '',
  `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`Host`,`User`,`Db`,`Table_name`),
  KEY `Grantor` (`Grantor`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Table privileges'
1 row in set (0.00 sec)

根据DDL 创建新表tables_priv_new 

INSERT INTO tables_priv_new SELECT * FROM tables_priv;

FLUSH TABLES tables_priv,tables_priv_new;

RENAME TABLE
    tables_priv TO tables_priv_old
   ,tables_priv_new TO tables_priv;

FLUSH PRIVILEGES;
方案三:检查和修复数据文件

适用场景: 怀疑数据文件损坏

# 1. 停止MySQL服务
net stop MySQL80

# 2. 备份重要数据目录
xcopy "C:\ProgramData\MySQL\MySQL Server 8.0\Data\mysql" "D:\Backup\mysql_backup" /E /H /K

# 3. 运行数据检查和修复
mysqlcheck -u root -p --all-databases --auto-repair

# 4. 重点关注权限表检查
mysqlcheck -u root -p mysql user db tables_priv columns_priv procs_priv --auto-repair
方案四:重新初始化数据目录(激进方案)

适用场景: 严重损坏,其他方案无效

# ⚠️ 警告:此操作会删除所有数据,必须提前备份!

# 1. 停止MySQL服务
net stop MySQL80

# 2. 备份整个数据目录
xcopy "C:\ProgramData\MySQL\MySQL Server 8.0\Data" "D:\Backup\mysql_full_backup" /E /H /K

# 3. 删除原数据目录内容
rmdir /S /Q "C:\ProgramData\MySQL\MySQL Server 8.0\Data"

# 4. 重新初始化
mysqld --initialize-insecure --console

# 5. 重启服务
net start MySQL80

# 6. 从备份中恢复业务数据

处理过程

安全模式启动

C:\Program Files\MySQL\MySQL Server 8.0\bin>mysqld --console --skip-grant-tables
2026-02-04T11:37:42.026345Z 0 [System] [MY-010116] [Server] C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe (mysqld 8.0.13) starting as process 2464
2026-02-04T11:37:44.487253Z 0 [System] [MY-010232] [Server] Crash recovery finished.
2026-02-04T11:37:45.726577Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.

结果: 数据库可以启动,但在执行 FLUSH PRIVILEGES 时再次崩溃。

由于权限表严重损坏,库也不大,采用导入导出

逻辑导出所有业务数据

# 安全模式启动参数
mysqld --console --skip-grant-tables --shared-memory

# 数据导出(排除系统库)
mysqldump -u root -p --all-databases --ignore-table=mysql.user --ignore-table=mysql.db > data_backup.sql

重新初始化MySQL实例

# 停止服务
net stop MySQL80

# 备份后删除数据目录
# 重新初始化
mysqld --initialize-insecure --console

# 重启服务
net start MySQL80

逻辑导入业务数据

mysql -u root -p < all_databases_backup.sql

— over —