【踩坑记录】MySQL InnoDB损坏/无法启动/数据丢失,通过binlog手动恢复数据

吾爱分享 建站运维评论10字数 1778阅读5分55秒阅读模式
【踩坑记录】MySQL InnoDB损坏/无法启动/数据丢失,通过binlog手动恢复数据插图

【实战记录】Mac mini 宕机导致 ServBay 下的 MySQL 数据丢失,使用时间机器备份还原整个系统后发现 InnoDB 引擎损坏导致 MySQL 无法成功启动,即使成功启动了,登录后也会频繁掉线,查看活动监视器后发现mysqld线程会频繁重启有时还会多个线程同时存在。即使能启动能登录不掉线了,导出数据库后查看sql文件结尾你会发现这样的提示“#2006 - MySQL server has gone away”,再仔细看你会发现数据库导出不完全,丢失了很多的内容,皆是因为这个报错导致了数据库导出过程中断。

经过一系列的原因排查、日志分析,确定是 MySQL InnoDB 引擎损坏,之后开始研究 MySQL 数据恢复,查找了各种恢复软件结果要么是开源项目停止维护了,要么是没有macOS系统软件。最后当我要放弃的时候,得知可以通过 binlog 的增删改日志来查询过往的数据库操作记录,最终在 ChatGPT 的帮助下,成功通过 mysqlbinlog 手动恢复了丢失的部分重要数据。

前言

我们总以为数据丢失这种事只会发生在别人身上,直到有一天它真真实实地砸在你头上。

这次我就经历了一次惨痛又有点曲折的数据丢失与恢复过程。所幸,我最终通过 MySQL binlog 成功手动找回了关键数据,但整个过程代价不小,费时费力,真的是一堂现实意义极强的教训课。

这篇文章将详细还原整个过程,希望能帮到你在面对类似 MySQL 数据崩溃、宕机或数据丢失问题时,能多一条思路,少一点恐慌。


背景信息

设备与环境

  • 设备:Mac mini M2(出事前),后用 M4 恢复
  • 数据库:MySQL 8.0(通过 ServBay 安装)
  • 应用:WordPress
  • 缓存插件:
    • WP Rocket(页面缓存)
    • Redis Object Cache(对象缓存)

事件起因

某天夜里打雷,家中遭遇电压浪涌,Mac mini M2 直接损坏,表现为“琥珀色灯闪烁”,系统无法正常启动。

我随后使用了 Time Machine 备份,将系统恢复到一块 USB4 外接硬盘中,并在新购入的 Mac mini M4 上启动。然而恢复后发现:

❌ MySQL 无法启动,InnoDB 报错,需要强制恢复才能勉强启动。


发现问题:MySQL 启动失败 + 数据丢失

尝试启动 MySQL 时,报错提示需要设置 innodb_force_recovery,我设置到最高等级:

innodb_force_recovery = 6
【踩坑记录】MySQL InnoDB损坏/无法启动/数据丢失,通过binlog手动恢复数据

这虽然让 MySQL 成功启动,但意味着InnoDB 引擎已严重损坏,处于只读状态,无法写入。

最可怕的是:当我查看数据库内容时,发现最近一段时间的重要数据全部丢失!


数据为什么会丢?

这背后有几个技术性原因:

  1. InnoDB 缓存机制
    InnoDB 使用缓冲池,数据并非实时写入磁盘,如果系统崩溃,未落盘的数据就会丢失。
  2. Redis Object Cache 缓存未持久化
    Redis 默认是内存数据库,如果你没设置 AOF/RDB,断电就会导致缓存数据完全丢失。
  3. Time Machine 备份有“延迟写盘”问题
    如果备份时数据尚未写入磁盘,备份中就只会存在“半写入”的数据库状态,导致恢复后数据不一致甚至损坏。

尝试解决办法

我尝试了多个方案:

  • ✅ 设置 innodb_force_recovery 启动 MySQL(只读模式)
  • ❌ 查阅 .ibd.frm 恢复:无效,数据页损坏
  • ✅ 启动 binlog 文件分析:看到了一线希望!

Binlog:关键救命稻草

幸运的是,我之前在 my.cnf 中启用了 binlog:

log_bin = mysql-bin
binlog_format = ROW

在目录中发现了关键文件:

<pre class="wp-block-syntaxhighlighter-code">/Applications/<a href="https://www.wuaishare.cn/tag/1171" title="查看与 ServBay 相关的文章" target="_blank"><span class="tag-key">ServBay</span></a>/db/mysql/8.4/mysql-bin.000007
</pre>

虽然文件体积接近 1GB,但里面包含了完整的 SQL 数据变更记录,只要能解析出来,就能还原数据。


如何解析 binlog 文件?

工具命令:

mysqlbinlog \
  --base64-output=DECODE-ROWS \
  --verbose \
  /路径/mysql-bin.000007 \
  > ~/Desktop/binlog_parsed.sql

这个命令会将 binlog 中的行级变更信息解析为标准 SQL,保存到文件中。

如何定位 wp_posts 相关数据?

  1. 使用 grepless 搜索关键字: grep -A 10 -B 10 'wp_posts' binlog_parsed.sql
  2. 手动复制出 INSERT 或 UPDATE SQL 语句
  3. 将其整理后执行到目标数据库中(生产站点)

下面是我实际用到的三段代码可作为参考:

前两个都是完整解析binlog文件,原理相同。

最后一个是因为我看原binlog文件有1G多太大了,所以想只解析一部分与"wp_posts"相关的内容,但最后我发现完整解析后虽然文件很大但是能查找和编辑,电脑也不卡顿,所以实际上我并没有使用最后那个解析出来的文件,全靠前面两个完整文件完成了数据恢复,当然如果你的数据库很大的话可能就需要根据时间段对数据进行拆分了,不然打开文件时编辑器很可能会卡死。

mysqlbinlog \
  --base64-output=DECODE-ROWS \
  --verbose \
  /Applications/ServBay/db/mysql/8.4/mysql-bin.000006 \
  > ~/Desktop/binlog_parsed_0719-0725.sql

mysqlbinlog \
  --base64-output=DECODE-ROWS \
  --verbose \
  /Applications/ServBay/db/mysql/8.4/mysql-bin.000007 \
  > ~/Desktop/binlog_parsed_0725-0730.sql

mysqlbinlog \
  --base64-output=DECODE-ROWS \
  --verbose \
  /Applications/ServBay/db/mysql/8.4/mysql-bin.000007 \
  | grep -A 150 -i "wp_posts" > ~/Desktop/wp_posts_binlog.sql

遇到的坑:

  • 某些字段为空字符串 '' 但字段类型是 INT,会报错: Incorrect integer value: '' for column xxx ✅ 解决办法:将 '' 替换为 NULL0

成功恢复!数据终于回来了

在小心翼翼地提取出 binlog 中有关 wp_postswp_term_relationshipswp_users 等表的 INSERT/UPDATE SQL 后,我一条条执行回写到生产站点。

虽然过程繁琐,但看到文章、交易数据等成功恢复的那一刻,所有的辛苦都值了!


后记:几点反思 & 建议

1. 一定要定期备份数据库本体(SQL)

Time Machine 并不能确保数据库备份的一致性。

✅ 推荐使用:

  • mysqldump 每日导出
  • 配合 binlog 实现增量恢复
  • 自动化脚本 + 云端存储(如 OSS、S3)

2. 开启 binlog + 使用 ROW 模式

log_bin = mysql-bin
binlog_format = ROW

配合 mysqlbinlog 工具,可以还原每一行数据。(宝塔面板中MySQL设置里的“二进制日志”就是binlog)


3. Redis 缓存 ≠ 持久化存储

如果你用 Redis 做对象缓存,别指望它能帮你保存数据库数据。请开启持久化(RDB + AOF)或只作为缓存使用。


4. UPS 不贵,数据无价

一次电涌可能让你损失几天甚至几个月的数据,一台 UPS 或稳定电源保护器远比你事后修复省心省钱。


结语

这次事件让我真正体会到什么叫做“数据无价”,也让我重新审视备份策略的重要性。希望这篇文章能帮到你,也欢迎转发给有需要的朋友。数据这东西,出问题时后悔永远是最晚的操作。

 
吾爱分享

发表评论