数据库崩了,谁遇上谁头大。上周三凌晨两点,我一个做电商的朋友打电话过来,声音都变了调——MySQL连不上,订单系统瘫了,后台报错一片红。他平时也没怎么备份,就靠着一台服务器硬扛,结果硬盘坏了,数据全卡在半空中。我问他有没有开binlog,他说开了,但没想过怎么用。这就是典型的“平时觉得没事,出事才慌”的场景。其实MySQL崩溃后恢复数据,远没你想的那么玄乎,关键是你得知道手边有什么牌可以打。今天我就把这几年踩过的坑、用过的招数掰开了聊,保证每一条都能落地。

先说说最常见的情况:MySQL服务启动不了。这时候别急着重装系统,先看一眼错误日志。默认路径在/var/log/mysql/error.log,或者你用查一下。日志里通常会告诉你具体卡在哪一步——比如“InnoDB: Corrupted page”或者“Can't find file: './xxx.frm'”。前者是数据页损坏,后者可能是表结构文件丢了。我见过有人看到报错就慌,直接重装覆盖了数据目录,结果本来能救的数据彻底没了。正确做法是先用强制启动,这个参数从1到6,数字越大恢复力度越强,但副作用也越大。从1开始试,能启动就赶紧用把数据导出来,再重建数据库。记住,强制恢复模式下只能读不能写,千万别手痒去改数据。
如果MySQL能启动,但某些表查不了,报“Table is marked as crashed”,那大概率是表文件损坏了。这种情况我碰过不下十回,多半是服务器突然断电或者磁盘满了导致的。修复方法很简单:用或者命令。MyISAM引擎的表可以直接在命令行跑,InnoDB的则用或者。但有个坑——如果表文件已经物理损坏,比如.frm文件被截断了,修复命令会直接报错。这时候你得从备份里拿结构文件,或者用工具从.ibd文件里抽取出表结构。我有个教训是:别等到表坏了才想起备份结构,每次建表后顺手导出一份的结果存起来,成本几乎为零。
说到备份,很多人以为有mysqldump就万事大吉了。但mysqldump有个致命弱点:它导出的是一整个SQL文件,如果数据库有几十GB,恢复时单线程执行慢得让人崩溃。我见过一个案例,公司用mysqldump做了全量备份,结果崩溃后恢复花了整整18个小时,业务直接停摆。更靠谱的做法是结合binlog做增量恢复。binlog默认开启了吗?没开的话赶紧在配置文件里加上和。崩溃后,先用全量备份恢复到一个时间点,然后通过工具把binlog里后续的操作重放出来。命令大概是。注意,binlog是按顺序写的,千万别漏了中间的文件,不然数据会错位。我习惯每天凌晨跑一次全量备份,白天每半小时记录一次binlog位置,这样恢复时最多丢30分钟的数据。
万一你连全量备份都没有,只剩数据文件了,怎么办?别慌,InnoDB有个特性叫“doublewrite buffer”,它会先写一个缓冲区再写数据文件,所以即使崩溃了,大部分数据页还是完整的。你只需要把数据库目录下的.ibd文件拷贝出来,然后在新实例上用和导入。但这招有个前提:表结构必须一致,而且InnoDB的page size要匹配。我试过从一台16K page size的服务器往8K的实例上导,结果直接报“Cannot find table in the tablespace”。解决办法是查看原服务器的参数,新实例必须设成一样的。还有,导入前别忘了先执行释放锁,不然文件会被占用。这招救急可以,但别指望100%成功,数据文件越完整,恢复概率越高。
再说一种更极端的情况:物理磁盘坏了,连数据目录都读不出来。这时候你得先判断是硬件故障还是文件系统损坏。如果是硬盘坏道,用或命令把整个磁盘镜像出来,然后挂载到另一台机器上读取。镜像命令是,它会跳过坏块,尽可能多救数据。我去年帮一个客户处理过这种情况,硬盘有300多个坏道,最终救回了95%的数据。如果是文件系统损坏,比如ext4报错,先别急着——这工具有时候会好心办坏事,把还在缓冲区的数据块标记成空闲。正确做法是用只读挂载,然后用命令从原始块设备里提取SQL语句。比如,能捞出来一部分插入语句。虽然不完整,但比啥都没有强。
说个很多人忽略的细节:恢复后一定要做数据校验。我见过有人用工具把数据救回来了,但没验证,结果上线后发现某些字段是乱的,比如用户手机号变成了乱码。原因是MySQL的字符集不一致——源库是utf8mb4,恢复时用了utf8,导致emoji和特殊字符被截断。校验方法很简单:写个脚本,随机抽取1%的记录,跟业务日志或者第三方对账平台比对。比如电商订单数据,挑出10笔订单,人工核对金额和状态。另外,恢复完成后要立刻检查优化表,因为恢复过程中会产生大量碎片,影响性能。还有一个习惯我坚持了五年:每次恢复后都做一次对比备份文件里的记录数,差一条都说明有问题。数据这东西,差之毫厘谬以千里,别嫌麻烦。
回头看我朋友那次事故,怎么解决的?他硬盘没坏,只是MySQL的InnoDB日志文件写满了导致崩溃。我远程连上去,先用强制启动,然后用mysqldump导出了所有数据,再把日志文件清空重建。整个过程花了不到40分钟,业务恢复了,但丢了一个小时的订单数据。他后来问我能不能找回那一个小时的数据,我问他binlog呢?他愣了半天说没开。所以你看,崩溃不可怕,可怕的是你连补救的牌都没备好。MySQL恢复这件事,说到底是“预案”二字:平时备份、开binlog、定期做恢复演练。别等火烧眉毛了才想起灭火器在哪。今天聊的这些方法,从强制启动到binlog重放,从物理镜像到字符集校验,每一条我都亲自试过,全是实战经验。你只要花半小时把配置改好、把命令存下来,下次崩溃时就能多救回几小时甚至几天的数据。


