上个月,我一个做电商的朋友半夜给我打电话,说服务器硬盘坏了,数据库全挂了。他急得声音都在抖,因为第二天早上8点要发货,所有订单数据都在那个 MySQL 数据库里。我让他别慌,先看看有没有备份。他沉默了几秒,说备份是有的,但上一次完整备份是三天前,这三天的订单全没了。那一刻,我隔着电话都能感受到他的绝望。这就是很多人对数据库备份的真实态度——知道重要,却从未认真对待。MySQL 作为最流行的开源数据库,每天都在帮无数人存着价值连城的数据,但真正会正确导出和恢复的人,其实没几个。

很多人觉得导出数据库不就是敲个 mysqldump 命令吗?确实,mysqldump 是最常用的工具,但用法差别大了去了。比如最简单的全库导出:。这个命令能把所有数据库都倒出来,但有个坑——默认情况下,它会把数据表和结构一起导出,文件会很大。如果只想导出结构不要数据,加 参数就行;反过来,只要数据不要结构,用 。还有个更实用的技巧,用 参数,这样导出的过程中不会锁表,生产环境也能安全执行。我见过太多人直接在线上跑 mysqldump,结果导出的几分钟里,网站所有写入操作都卡住了,用户骂声一片。
除了 mysqldump,MySQL 其实还提供了其他导出方式,比如 。这个命令能把查询结果直接导出成文本文件,每行一条记录,字段用制表符隔开。它的好处是特别灵活,你想导出哪些字段、什么格式,完全由你控制。比如:这样就得到一个标准的 CSV 文件,Excel 可以直接打开。但有个坑要注意——OUTFILE 只能在 MySQL 服务器本地生成文件,不能远程导出。而且 MySQL 用户必须有 FILE 权限,默认安装情况下,普通用户是没有这个权限的。很多新手在这里卡住,报错说 “Access denied”,还以为自己命令敲错了。
说实话,导出数据只是第一步,真正考验人的是恢复。我见过最惨的案例,一个朋友把备份文件下载下来,解压一看是个 .gz 压缩包。他用 gunzip 解压后得到一个 .sql 文件,然后兴致勃勃地敲 。结果等了半小时没反应,报了个 “MySQL server has gone away”。为什么?因为他的备份文件有 10 GB,而 MySQL 默认的 只有 64 M,太大的 SQL 语句直接被服务器拒绝。解决办法很简单:在恢复之前,先把 调大到 1 G,或者在命令行里加 参数。这个坑几乎每个人都踩过,包括我自己。第一次恢复一个 300 M 的备份时,也遇到同样的报错,折腾了半个小时才找到答案。
还有一个很多人不知道的恢复技巧,就是分步恢复。比如你有一个 100 GB 的备份文件,想一次性恢复,不仅耗时长,而且万一中途出错,就得从头再来。聪明的做法是把备份文件拆成多个小文件。mysqldump 导出的 SQL 文件里,每个表的数据都是连续的一段,你可以用文本编辑器或 命令按表分割。更简单的方法是使用 mysql 命令行的分段执行:。加上 参数后,即使某个表恢复失败,也不会中断整个过程,其他表还能继续。这个参数在线上环境特别有用,尤其是当你接手一个不确定是否有问题的备份文件时。
说到恢复,还有一个让人头疼的场景——跨版本恢复。比如你从 MySQL 5.7 导出数据,想恢复到 MySQL 8.0。直接恢复大概率会报错,因为 8.0 的很多默认设置变了,sqlmode 更严格。最常见的问题是 “Invalid default value for ‘createdat’”,因为 5.7 里允许 字段默认值为 ,但 8.0 不允许。解决办法是在恢复之前,先修改会话级别的 sqlmode:;或者更干脆一点,在导出时加 参数。我的习惯是,无论目标版本是什么,导出时都加上 ,这样生成的 SQL 文件兼容性更好,省得后面折腾。
我想说一个很多人忽略但极其重要的事情——测试恢复。你辛辛苦苦做了备份,但备份文件能不能用,只有真正恢复一次才知道。我有个朋友,公司规定每天凌晨 2 点自动备份,他两年里从未出过问题。直到有一天数据库真的坏了,他兴冲冲地拿出备份恢复,结果发现备份文件是空的。因为自动备份脚本里有个 bug,备份时磁盘空间不足,生成了一个 0 字节的文件,脚本却没有报错,还显示备份成功。从那以后,我给自己定了个规矩:每个月至少手工恢复一次备份,而且在测试环境里进行,检查数据是否完整。听起来麻烦,但比起数据丢失后求爷爷告奶奶的狼狈,这点时间太值得了。记住,备份不是为了备份,而是为了恢复。


