数据库备份应该怎么做?

先说个血泪教训:备份了不等于能恢复。

我见过有人备份脚本跑了一年,真出事了发现备份文件全是空的——脚本报错没人看。

所以备份这事,分两步:怎么备怎么验


备份的几个层次

从简单到复杂,看你的数据值多少钱。

最基础:mysqldump定时导出

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
mysqldump -u backup -p'password' \
    --all-databases \
    --single-transaction \
    --routines --triggers --events \
    | gzip > /data/backup/all_db_$DATE.sql.gz

# 删除7天前的备份
find /data/backup -name "*.sql.gz" -mtime +7 -delete

扔到crontab里,每天凌晨跑一次:

0 3 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1

这个方案的问题:备份只在本地,服务器挂了或者机房出事,备份也没了。

进阶:备份传到云存储

本地备份完,传一份到云上:

# 阿里云OSS
ossutil cp /data/backup/all_db_$DATE.sql.gz oss://your-bucket/mysql/

# 或者AWS S3
aws s3 cp /data/backup/all_db_$DATE.sql.gz s3://your-bucket/mysql/

云存储很便宜,几十G也就几块钱一个月。

再进阶:binlog实时备份

mysqldump是某个时间点的快照。如果早上3点备份,下午2点数据库挂了,这中间11小时的数据就丢了。

想要更精确的恢复,要备份binlog:

# 开启binlog(my.cnf)
[mysqld]
log-bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7

# 实时拉取binlog到备份服务器
mysqlbinlog --read-from-remote-server \
    --host=主库IP \
    --raw --stop-never \
    --result-file=/data/binlog/ \
    mysql-bin.000001

恢复的时候,先恢复全量备份,再把binlog应用到指定时间点:

mysql < full_backup.sql
mysqlbinlog --stop-datetime="2024-01-15 14:30:00" binlog.* | mysql

这样就能恢复到下午2点30分那一刻的状态。


异地备份:真正的保险

上面说的都是本地+云存储。但如果你的云账号被黑了呢?或者云厂商出问题呢?

3-2-1原则:3份数据、2种介质、1份异地。

我自己的做法是在家里放了台NAS,跑一个MySQL从库,实时同步公司服务器的数据。

问题是:家里没有公网IP,公司服务器怎么连到家里的NAS?

我用的是组网方案,把公司服务器和家里NAS组成虚拟局域网。配置好之后,MySQL主从复制就像在同一个内网:

-- 家里NAS上的MySQL配置
CHANGE MASTER TO
    MASTER_HOST = '10.26.0.1',   -- 组网的内网IP
    MASTER_USER = 'repl',
    MASTER_PASSWORD = 'xxx',
    MASTER_LOG_FILE = 'mysql-bin.000001',
    MASTER_LOG_POS = 154;
START SLAVE;

这样即使公司服务器和云存储同时出问题,家里还有一份实时同步的数据。

我用的星空组网,配置比自己搭frp简单。当然你要是有公网IP的云服务器,自己搭也行。


备份验证:最容易忽略的一步

备份了不验证,等于没备份。

定期做恢复测试

# 下载最新备份
ossutil cp oss://bucket/mysql/latest.sql.gz /tmp/

# 解压
gunzip /tmp/latest.sql.gz

# 恢复到测试库
mysql -h test-server -u root -p test_db < /tmp/latest.sql

# 验证:查几个关键表的数据量
mysql -e "SELECT COUNT(*) FROM test_db.users;"
mysql -e "SELECT COUNT(*) FROM test_db.orders;"

建议每月至少做一次恢复测试

检查备份文件完整性

# 检查gzip文件是否损坏
gunzip -t backup.sql.gz

# 检查SQL文件结尾是否正常
tail -5 backup.sql
# 正常应该有:-- Dump completed on 2024-01-15 03:00:01

我之前就遇到过备份脚本因为磁盘满了,导出到一半就停了,文件看着有,其实是残的。


云数据库的备份

如果用的是阿里云RDS、腾讯云这些,它们自带备份功能,省心但要花钱。

优点

  • 自动备份,不用操心
  • 跨地域备份一键开启
  • 恢复方便,控制台点点就行

缺点

  • 要花钱
  • 数据在别人手里
  • 被厂商绑定

我的建议是:云数据库的自动备份开着,但自己也拉一份到本地或者其他地方。鸡蛋不要放一个篮子里。


我的备份策略

分享一下我实际在用的:

每天凌晨3点:mysqldump全量备份
    ↓
上传阿里云OSS,保留30天
    ↓
家里NAS跑MySQL从库,通过组网实时同步
    ↓
binlog也同步到NAS

出事了怎么恢复:

  • 误删数据 → 从OSS下载最近的全量 + binlog恢复到指定时间点
  • 服务器挂了 → 切到家里的从库顶一下
  • 云厂商跑路 → 还有家里的完整备份

成本:OSS一个月几块钱 + 家里本来就有的NAS。心里踏实。


最后

备份这事,没出事的时候觉得是成本,出事了才知道是救命的。

几个原则:

  1. 本地备份 + 云存储 + 异地,至少两个
  2. 定期验证,每月恢复测试一次
  3. 监控告警,备份失败要能第一时间知道
  4. binlog如果数据重要,考虑实时备份

宁可备份用不上,不能用的时候没有。