上周帮一个朋友恢复数据库,折腾到凌晨三点。他做电商的,凌晨两点发现数据库挂了,订单表全没了,急得差点报警。这事儿让我想起自己刚入行时,不小心 drop 了一个生产库,领导脸色铁青,我手心冒汗,硬着头皮熬了两个通宵。Linux 下的 MySQL 数据库恢复,说起来复杂,其实就那么几招,关键看你会不会用。

先说最基础的情况:误删数据。很多人以为删了就没了,其实 MySQL 有个叫 binlog 的东西,记录着所有写操作。只要 binlog 没关,就能通过它恢复。我见过一个运维,删了整张表,吓得当场辞职。其实只要执行 看看有哪些日志文件,再用 工具解析,找到删除前的时间点,导出 SQL 重新跑一遍就行。有个细节:binlog 格式要是 ROW 级别的,才能精准恢复单行数据。STATEMENT 格式只能恢复整个事务,MIXED 格式则看运气。所以建库时记得设置 ,这能救你命。
再说文件损坏的情况。有一次客户服务器硬盘出现坏道,MySQL 数据文件读不出来,启动就报错。这种情况别慌,先检查 ,看具体是哪个表损坏。如果只是个别表,用 指令,MySQL 自带的修复工具能搞定大部分问题。要是整个库都坏了,就得靠物理备份。我习惯用 mysqldump 每天凌晨全量备份,再加上 binlog 增量备份。恢复时先把全量备份导入,再通过 binlog 恢复到故障前的那一刻。关键点是:恢复前要确认备份文件的时间戳和 binlog 的起点一致,否则数据会乱。我之前因为时区设置错误,恢复出来的数据全错,查了半天才发现是 UTC 与 CST 的差异。
还有一种情况更棘手:MySQL 实例完全崩溃,连启动都不行。这多半是系统表损坏,比如 表坏了,所有账号都登不上去。解决办法是启动时加上 参数,跳过权限验证,然后手动修复系统表。但要注意,这样操作后数据库是裸奔状态,任何人连上都能干任何事,修复完要立刻重启正常模式。我有次修复完忘了关这个参数,结果被公司安全部门通报批评。还有个“骚操作”:如果数据文件还在,但 InnoDB 引擎报错,可以把 参数设为 1 到 6,逐步增加力度。设为 1 时跳过损坏页,设为 6 时跳过所有检查,但会丢失部分数据。这个参数就像止痛药,能让你启动起来把数据导出来,但别长期开着。
冷备份和热备份的区别,很多人搞不清楚。冷备份就是停掉 MySQL 服务,直接拷贝数据目录。优点是简单粗暴,恢复时直接覆盖就行;缺点是停机时间长,大库可能要停几小时。热备份可以用第三方工具,比如 Percona XtraBackup,能在不停机的情况下做物理备份。我遇到过一个场景:数据库 500 GB,冷备份要停服务 4 小时,老板不同意。后来用 XtraBackup,在线备份只用了 20 分钟,恢复也快,因为它基于文件级别的增量备份。但热备份有个坑:备份过程中如果有 DDL 操作,比如修改表结构,会导致备份失败。所以备份脚本里最好加上 临时锁表,虽然会短暂阻塞写入,但总比备份失败强。
说到备份策略,我见过最离谱的方案:有人把备份文件放在和数据库同一块硬盘上。硬盘坏时,数据没了,备份也没了,这叫“备份了个寂寞”。正确的做法是异地备份,至少跨机房,最好跨地域。我用 Amazon S3 存备份文件,设置生命周期策略,自动删除 90 天前的旧备份。但云存储也有问题:网络带宽不足时,备份上传会很久。500 GB 的数据,100 M 带宽要传 11 小时,这期间如果数据库出问题,你只能干瞪眼。所以我现在采用增量备份方案:第一次全量,后面每天只传变化的那部分,大小可能只有几 GB,几分钟就能搞定。
恢复时的顺序也容易出错。很多人拿到备份文件,直接解压覆盖,然后启动 MySQL,结果报错一堆。正确顺序是:先停掉 MySQL 服务,清空数据目录,然后把备份文件解压到数据目录,修改权限为 mysql 用户,再启动服务。有个细节:备份文件里的配置文件 可能和当前系统不兼容,比如 不同,会导致启动失败。因此恢复前最好先检查旧配置和新环境的差异。我吃过这个亏:从测试环境备份的数据库,恢复到生产环境时,因为字符集不同,中文全变成乱码,只好用 一张表一张表改,花了大半天。
说个冷门但实用的技巧:用 LVM 快照恢复。如果你的数据库在 LVM 逻辑卷上,可以用 创建快照,然后挂载快照,直接拷贝数据文件。这个过程几乎不影响数据库性能,恢复时也很快。我曾在线上环境用这个办法,5 分钟就完成了 10 GB 数据库的恢复,业务几乎无感知。但快照有个缺点:空间满了会失效,所以创建时要留足空间,一般设置为原卷的 20% 到 30%。还有前提是文件系统要支持快照,ext4、xfs 都行,但 btrfs 有坑,我试过两次都失败了。
回到开头那个朋友的问题。那天凌晨三点,我远程连上他的服务器,发现是 binlog 没开,mysqldump 备份也过期了,只能从硬盘坏道里抢救数据。用 把损坏的硬盘镜像出来,再用 命令从二进制文件里捞数据,花了 5 个小时,恢复了 80% 的订单。结果不算完美,但总比全丢强。他后来请我吃了顿火锅,席间感慨:“早知道就听你的,每天备份一次。”我夹了片毛肚,没接话。备份这事儿,没人能替你疼。


