Press "Enter" to skip to content

【实战记录】Docker MySQL启动报错“privilege tables not found”的紧急数据救援指南

最近维护的一个基于 Docker 部署的 MySQL 5.7 服务突然无法启动,导致业务中断。查看日志发现是一连串的致命错误,提示系统权限表丢失。

面对这种数据库起不来、系统表损坏的情况,当务之急不是“完美修复环境”,而是先把数据导出来。本文记录了从报错分析到进入“救援模式”并成功导出数据的完整过程。

一、 故障现象

容器启动后立即退出,处于 Restart Loop(无限重启)状态。查看 Docker 日志:

docker-compose logs -f db

关键报错信息如下:

...
mysqld: Out of memory (Needed 4294967200 bytes)
...
[ERROR] Fatal error: Can't open and lock privilege tables: File './mysql/columns_priv.MYD' not found (Errcode: 2 - No such file or directory)
[ERROR] Fatal: can't initialize grant subsystem - 'File './mysql/columns_priv.MYD' not found'
[ERROR] Aborting

二、 问题分析

  1. OOM (Out of Memory): 日志开头出现了 Out of memory,说明宿主机或容器内存不足,导致 MySQL 进程曾被强制杀掉。
  2. 文件损坏: 随后出现的 Fatal error: Can't open and lock privilege tables 是最致命的。这说明 MySQL 的核心系统库(mysql 库)中的权限表文件(如 columns_priv.MYD)丢失或损坏。

因为权限表损坏,MySQL 无法验证用户身份,所以无法正常启动。

三、 救援方案

既然正常模式无法启动,我们的目标是:

  1. 物理备份(防止误操作导致全军覆没)。
  2. 开启 MySQL 的“跳过权限检查”模式,强行启动。
  3. 使用 mysqldump 导出业务数据。
  4. 重建干净的数据库实例并导入数据。

四、 操作步骤

第一步:物理文件冷备份(最重要!)

不管能不能修好,先保留案发现场。直接在宿主机上把挂载的卷目录复制一份。

# 假设原来的挂载目录是 /home/volume/wp-bak/db/
cp -r /home/volume/wp-bak/db/ /home/volume/wp-bak/db_backup_raw/

注意:如果后续操作失败,我们还可以尝试从这个 raw backup 中恢复 .ibd 文件。

第二步:修改 docker-compose 进入“救援模式”

我们需要修改 docker-compose.yml,给 MySQL 容器加上启动参数,让它忽略权限表并尝试强制恢复。

修改 docker-compose.yml

services:
   db:
     image: mysql:5.7
     ports:
       - "13202:3306"
     volumes:
        - /home/volume/wp-bak/db/:/var/lib/mysql
     # 【新增】添加下面这行 command
     command: --skip-grant-tables --innodb_force_recovery=1
     restart: always

参数解释:

  • --skip-grant-tables: 不加载权限表(从而绕过 columns_priv.MYD 丢失的报错),允许无密码登录。
  • --innodb_force_recovery=1: 让 InnoDB 引擎以只读/低恢复模式启动,忽略部分损坏页,增加启动成功的概率。

第三步:启动并导出数据

应用修改并启动容器:

docker-compose up -d

查看日志确认是否存活:

docker-compose logs -f db
# 只要看到 "ready for connections" 或者没有立即退出,就说明救援模式启动成功

进入容器导出数据(无需密码):

# 1. 进入容器
docker exec -it <容器ID或名称> /bin/bash

# 2. 确认数据库还在
mysql -u root -e "SHOW DATABASES;"

# 3. 导出业务数据库(例如库名叫 wordpress)
# 注意:因为开启了 skip-grant-tables,不需要输入密码
mysqldump -u root --default-character-set=utf8mb4 wordpress > /var/lib/mysql/wordpress_rescue.sql

此时,导出的 SQL 文件 wordpress_rescue.sql 已经保存在宿主机的挂载目录下了。

第四步:彻底重建

既然系统表已经损坏,继续使用这个环境是不安全的。建议彻底重建。

  1. 停止容器:docker-compose down
  2. 移除旧的数据目录(确保你已经有了第一步的备份和第三步的 SQL 文件!):rm -rf /home/volume/xiaocaicai-wp-bak/db/*
  3. 去掉 docker-compose.yml 中的 command 行,恢复正常配置。
  4. 重新启动容器,此时 MySQL 会重新初始化生成全新的数据文件。
  5. 将刚才导出的 wordpress_rescue.sql 导入新数据库。

五、 避坑指南与总结

  1. 内存限制:这次事故的诱因是 OOM。建议在 docker-compose.yml 中给数据库容器加上内存限制(mem_limit),或者增加服务器的 Swap 分区,防止 MySQL 吃光内存被系统 Kill 导致文件损坏。
  2. 数据备份:定期的逻辑备份(mysqldump)比物理挂载更可靠。可以编写简单的 Shell 脚本每天定时导出 SQL。
  3. 冷静分析:看到 Fatal Error 不要慌,只要 .ibd(数据文件)还在,就有办法恢复。skip-grant-tables 是处理权限相关错误的万能钥匙。

希望这篇记录能帮到同样遇到 MySQL 崩溃的朋友!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注