MySQL: MySQL 备份与恢复完全指南
最后更新:2026-08-26
Alice 的公司服务器硬盘突然损坏,数据库彻底崩溃。没有备份,3 年的客户数据、订单记录、财务信息永远丢失。这次灾难让她痛定思痛,建立了完善的备份体系:每日全量 mysqldump + binlog 持续增量 + 每周异地传输 + 每月恢复演练。从那以后,即便再次遭遇硬件故障,她也能在 30 分钟内完整恢复所有数据。
本课你将学到:
- mysqldump 逻辑备份的 5 种模式(全量/单库/单表/结构 only/数据 only)
- mysql 命令恢复与 SOURCE 恢复的操作方法
- SELECT INTO OUTFILE 导出与 LOAD DATA INFILE 导入
- 二进制日志 binlog 与时间点恢复(PITR)的完整流程
- 物理备份 XtraBackup 原理及备份策略设计
flowchart TD
A[每日全量备份<br/>mysqldump --single-transaction] --> B[binlog 持续记录<br/>实时增量]
B --> C[每周异地传输<br/>rsync/scp 至远程]
C --> D[每月恢复演练<br/>验证备份可用性]
D -->|演练通过| A
D -->|发现问题| E[调整备份策略]
E --> A
1. 故事:没有备份的代价
Alice 所在的 TechFlow 公司运行着一套 MySQL 电商平台数据库,存储了 3 年的客户订单与账户信息。运维团队从未建立正式的备份流程,仅靠 RAID 磁盘阵列作为"保障"。一个周五晚上,机房空调故障导致服务器过热,两块硬盘同时损坏,RAID 阵列崩溃。所有数据——客户信息、交易记录、库存数据——全部丢失。公司花费了 3 个月尝试数据恢复,仅找回不到 10%。直接经济损失超过 500,000 USD,更失去了大量客户信任。
这次惨痛教训后,Alice 主导建立了完整的备份体系:每日凌晨自动全量备份、binlog 实时增量、每周异地同步、每月恢复演练。半年后服务器再次故障,这次她在 30 分钟内完成了完整恢复,业务几乎零中断。
2. 逻辑备份与物理备份
(1) 逻辑备份概述
逻辑备份将数据库中的数据导出为 SQL 文本文件,包含 CREATE TABLE、INSERT 等语句。恢复时重新执行这些 SQL 即可重建数据。代表工具:mysqldump。
优点: 可读性强、跨版本兼容、可选择性备份单库单表。
缺点: 速度慢、备份和恢复都需要 MySQL 进程参与、大库耗时长。
(2) 物理备份概述
物理备份直接复制数据库的底层数据文件(.ibd、.frm 等),恢复时将文件复制回数据目录即可。代表工具:XtraBackup。
优点: 速度快、不锁表(InnoDB)、适合大库。
缺点: 不可读、跨平台受限、需停机或使用专用工具保持一致性。
| 对比项 | 逻辑备份(mysqldump) | 物理备份(XtraBackup) |
|---|---|---|
| 备份内容 | SQL 文本语句 | 数据文件副本 |
| 备份速度 | 慢(逐行导出) | 快(文件复制) |
| 恢复速度 | 慢(逐行执行 SQL) | 快(文件复制回) |
| 锁表影响 | InnoDB 可免锁 | 不锁表 |
| 可读性 | 高(纯文本 SQL) | 低(二进制文件) |
| 存储开销 | 小(文本压缩率高) | 大(完整文件副本) |
| 跨版本 | 兼容 | 需版本匹配 |
| 适用场景 | 中小型库 ≤ 50 GB | 大型库 > 50 GB |
3. mysqldump 逻辑备份详解
(1) 全量备份
全量备份导出所有数据库的完整数据,是最基础也是最安全的备份方式。
mysqldump -u root -p --all-databases --single-transaction > full_backup.sql
--single-transaction 参数在 InnoDB 表上使用一致性快照读取,不会锁表。MyISAM 表仍会锁表。
(2) 单库与单表备份
# 备份单个数据库
mysqldump -u root -p --single-transaction mydb > mydb_backup.sql
# 备份指定表
mysqldump -u root -p --single-transaction mydb users orders > tables_backup.sql
(3) 结构与数据分离
# 只备份表结构
mysqldump -u root -p --no-data mydb > mydb_schema.sql
# 只备份数据
mysqldump -u root -p --no-create-info mydb > mydb_data.sql
▶ 示例:mysqldump 五种备份模式
# 模式1:全量备份(所有库)
mysqldump -u root -p --all-databases \
--single-transaction --routines --triggers \
> full_backup_$(date +%Y%m%d).sql
# 模式2:单库备份
mysqldump -u root -p --single-transaction mydb > mydb.sql
# 模式3:单表备份
mysqldump -u root -p --single-transaction mydb users > users.sql
# 模式4:仅结构
mysqldump -u root -p --no-data mydb > schema.sql
# 模式5:仅数据
mysqldump -u root -p --no-create-info mydb > data.sql
| 参数 | 作用 | 适用场景 |
|---|---|---|
--all-databases |
备份所有数据库 | 全量备份 |
--single-transaction |
InnoDB 一致性快照 | 在线备份不锁表 |
--no-data |
仅导出表结构 | 文档记录、建库脚本 |
--no-create-info |
仅导出数据 | 数据迁移 |
--routines |
包含存储过程和函数 | 完整逻辑备份 |
--triggers |
包含触发器 | 完整逻辑备份 |
--where="condition" |
按条件导出部分行 | 部分数据迁移 |
--quick |
逐行读取不缓存 | 大表备份防 OOM |
4. 恢复数据库
(1) mysql 命令恢复
从备份 SQL 文件恢复数据到指定数据库:
mysql -u root -p mydb < mydb_backup.sql
恢复压缩备份文件:
gunzip < mydb_backup.sql.gz | mysql -u root -p mydb
(2) SOURCE 命令恢复
在 MySQL 客户端内使用 SOURCE 命令恢复:
USE mydb;
SOURCE /var/backups/mysql/mydb_backup.sql;
▶ 示例:恢复流程
# 步骤1:创建目标数据库
mysql -u root -p -e "CREATE DATABASE mydb_restore;"
# 步骤2:恢复数据
mysql -u root -p mydb_restore < mydb_backup.sql
# 步骤3:验证行数
mysql -u root -p -e "SELECT COUNT(*) FROM mydb_restore.users;"
▶ 示例:恢复压缩备份
# 恢复 gzip 压缩备份
gunzip < /backup/mydb_20260703.sql.gz | mysql -u root -p mydb
# 恢复并显示进度
pv /backup/mydb_20260703.sql.gz | gunzip | mysql -u root -p mydb
5. 数据导出与导入
(1) SELECT INTO OUTFILE
将查询结果导出为服务器端的 CSV 文件:
SELECT id, name, email
FROM users
WHERE status = 'active'
INTO OUTFILE '/tmp/active_users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
注意:
INTO OUTFILE的路径是 MySQL 服务器 上的路径,不是客户端路径。MySQL 进程对该路径必须有写权限,且secure_file_priv变量必须允许该目录。
(2) LOAD DATA INFILE
从服务器端 CSV 文件导入数据到表:
LOAD DATA INFILE '/tmp/active_users.csv'
INTO TABLE users_copy
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
▶ 示例:CSV 导出导入完整流程
-- 导出订单数据为 CSV
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= '2026-01-01'
INTO OUTFILE '/tmp/orders_2026.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n';
-- 导入到另一张表
LOAD DATA INFILE '/tmp/orders_2026.csv'
INTO TABLE orders_archive
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n';
▶ 示例:使用 LOCAL 导入客户端文件
LOAD DATA LOCAL INFILE '/home/alice/data/products.csv'
INTO TABLE products
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
6. 二进制日志与时间点恢复
(1) binlog 基础
二进制日志(binary log)记录所有更改数据的 SQL 语句,主要用于主从复制和时间点恢复(Point-in-Time Recovery, PITR)。
-- 查看 binlog 是否开启
SHOW VARIABLES LIKE 'log_bin%';
-- 查看当前 binlog 文件列表
SHOW BINARY LOGS;
-- 查看当前正在写入的 binlog
SHOW MASTER STATUS;
-- 查看 binlog 事件内容
SHOW BINLOG EVENTS IN 'binlog.000003';
binlog 三种格式:
| 格式 | 记录内容 | 优缺点 |
|---|---|---|
| STATEMENT | 记录 SQL 语句 | 日志小,但不确定语句可能不一致 |
| ROW | 记录行变更 | 日志大,但数据一致性好 |
| MIXED | 混合模式 | 默认用 STATEMENT,不确定时切 ROW |
(2) 时间点恢复流程
时间点恢复的核心思路:先恢复最近的全量备份,再重放 binlog 到目标时间点。
# 步骤1:恢复全量备份
mysql -u root -p mydb < full_backup_20260703.sql
# 步骤2:找到误操作之前的时间点
mysqlbinlog --base64-output=DECODE-ROWS -v binlog.000003 | grep -A5 "DROP TABLE"
# 步骤3:重放 binlog 到目标时间点
mysqlbinlog --start-datetime="2026-07-03 02:00:00" \
--stop-datetime="2026-07-03 14:30:00" \
binlog.000003 binlog.000004 | mysql -u root -p
▶ 示例:误删表后的时间点恢复
场景:14:25 误执行了 DROP TABLE orders,需要恢复到 14:24:59 的状态
时间线:
02:00 全量备份完成
...
14:24 最后一次正常操作
14:25 DROP TABLE orders(误操作)
14:30 发现问题,开始恢复
恢复步骤:
1. 恢复 02:00 全量备份
2. 用 mysqlbinlog 重放 02:00 至 14:24:59 的 binlog
3. 验证 orders 表数据完整性
# 实际执行命令
mysql -u root -p mydb < /backup/full_20260703.sql
mysqlbinlog --start-datetime="2026-07-03 02:00:00" \
--stop-datetime="2026-07-03 14:24:59" \
/var/lib/mysql/binlog.000003 \
/var/lib/mysql/binlog.000004 \
| mysql -u root -p mydb
7. 物理备份 XtraBackup
(1) XtraBackup 工作原理
XtraBackup 是 Percona 开发的开源物理备份工具,核心原理:
- 后台线程 持续读取 InnoDB 数据页,同时监控 redo log 变化
- 备份期间新产生的 redo log 也被持续记录
- 备份结束后,用 redo log 前滚数据页到一致性状态
- 全程不锁表,不影响业务读写
(2) 基本操作
# 全量备份
xtrabackup --backup --target-dir=/backup/full -u root -p
# 准备备份(应用 redo log,使数据一致)
xtrabackup --prepare --target-dir=/backup/full
# 恢复备份
xtrabackup --copy-back --target-dir=/backup/full
# 增量备份(基于全量)
xtrabackup --backup --target-dir=/backup/inc1 \
--incremental-basedir=/backup/full -u root -p
# 准备增量备份
xtrabackup --prepare --apply-log-only --target-dir=/backup/full
xtrabackup --prepare --target-dir=/backup/full \
--incremental-dir=/backup/inc1
▶ 示例:XtraBackup 全量备份与恢复
# 全量备份
xtrabackup --backup \
--target-dir=/data/backup/full_20260703 \
--user=root --password=Secret123
# 准备阶段
xtrabackup --prepare \
--target-dir=/data/backup/full_20260703
# 恢复前停止 MySQL
systemctl stop mysqld
# 清空数据目录并恢复
rm -rf /var/lib/mysql/*
xtrabackup --copy-back \
--target-dir=/data/backup/full_20260703
# 修复权限并启动
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
8. 备份策略设计
(1) 全量与增量组合
- 每日全量 + binlog 实时增量:适合中小型库,恢复简单
- 每周全量 + 每日增量(XtraBackup):适合大型库,节省空间
(2) 异地备份
备份文件必须传输到异地存储,防止机房级灾难:
# 使用 rsync 传输到远程
rsync -avz /backup/mysql/ backup-server:/data/mysql_backup/
# 使用 scp 传输
scp /backup/mysql/full_20260703.sql.gz backup-server:/data/backup/
(3) 定期恢复演练
备份不验证等于没有备份。至少每月进行一次完整的恢复演练,确认:
- 备份文件可以正常解压和恢复
- 恢复后数据完整性校验通过
- 恢复时间在可接受的 RTO 范围内
| 数据规模 | 全量频率 | 增量方式 | 异地策略 | 恢复演练 |
|---|---|---|---|---|
| ≤ 10 GB | 每日全量 | binlog | 每日传输 | 每月一次 |
| 10-100 GB | 每日全量 | binlog | 每日传输 | 每月一次 |
| 100 GB - 1 TB | 每周全量 | 每日 XtraBackup 增量 | 每周传输 | 每季一次 |
| > 1 TB | 每周全量 | 每日 XtraBackup 增量 | 实时同步 | 每季一次 |
| 恢复方式 | 适用场景 | 恢复速度 | 操作复杂度 | 数据粒度 |
|---|---|---|---|---|
| mysqldump 全量恢复 | 误删库/全库迁移 | 慢 | 低 | 全库 |
| binlog 时间点恢复 | 误操作回滚 | 中 | 高 | 秒级 |
| XtraBackup 恢复 | 硬件故障/灾难 | 快 | 中 | 全库 |
| LOAD DATA INFILE | 单表数据补录 | 快 | 低 | 单表 |
9. 自动化备份与综合示例
(1) 自动化备份要点
- 使用 crontab 定时执行备份脚本
- 备份文件按日期命名,便于查找
- 自动压缩节省空间
- 定期清理过期备份(保留 N 天)
- 备份完成后发送通知
▶ 示例:自动化备份脚本
#!/bin/bash
# mysql_backup.sh - MySQL 自动化备份脚本
BACKUP_DIR="/data/backup/mysql"
RETAIN_DAYS=7
DB_USER="root"
DB_PASS="Secret123"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/mysql_backup.log"
mkdir -p $BACKUP_DIR
echo "[$DATE] 开始备份..." >> $LOG_FILE
mysqldump -u$DB_USER -p$DB_PASS \
--all-databases --single-transaction \
--routines --triggers --set-gtid-purged=OFF \
| gzip > $BACKUP_DIR/full_${DATE}.sql.gz
if [ $? -eq 0 ]; then
SIZE=$(du -sh $BACKUP_DIR/full_${DATE}.sql.gz | cut -f1)
echo "[$DATE] 备份成功,大小: $SIZE" >> $LOG_FILE
else
echo "[$DATE] 备份失败!" >> $LOG_FILE
exit 1
fi
find $BACKUP_DIR -name "full_*.sql.gz" -mtime +$RETAIN_DAYS -delete
echo "[$DATE] 清理 ${RETAIN_DAYS} 天前的备份" >> $LOG_FILE
▶ 示例:完整备份恢复脚本
#!/bin/bash
# full_backup_restore.sh - 完整备份与恢复验证脚本
BACKUP_DIR="/data/backup/mysql"
MYSQL_USER="root"
MYSQL_PASS="Secret123"
REMOTE_HOST="backup-server"
REMOTE_DIR="/data/mysql_backup"
DATE=$(date +%Y%m%d)
LOG="/var/log/backup_restore.log"
echo "=== $DATE 备份恢复流程 ===" >> $LOG
# 1. 全量备份
mysqldump -u$MYSQL_USER -p$MYSQL_PASS \
--all-databases --single-transaction \
--routines --triggers \
| gzip > $BACKUP_DIR/full_${DATE}.sql.gz
echo "[$DATE] 全量备份完成" >> $LOG
# 2. 刷新并记录 binlog 位置
mysql -u$MYSQL_USER -p$MYSQL_PASS -e "FLUSH BINARY LOGS;"
BINLOG_FILE=$(mysql -u$MYSQL_USER -p$MYSQL_PASS \
-N -e "SHOW MASTER STATUS;" | awk '{print $1}')
echo "[$DATE] 当前 binlog: $BINLOG_FILE" >> $LOG
# 3. 备份 binlog 文件
cp /var/lib/mysql/$BINLOG_FILE $BACKUP_DIR/
echo "[$DATE] binlog 已备份" >> $LOG
# 4. 异地传输
rsync -avz $BACKUP_DIR/full_${DATE}.sql.gz \
$REMOTE_HOST:$REMOTE_DIR/ >> $LOG 2>&1
echo "[$DATE] 异地传输完成" >> $LOG
# 5. 恢复验证(在临时库)
mysql -u$MYSQL_USER -p$MYSQL_PASS \
-e "CREATE DATABASE IF NOT EXISTS verify_db;"
gunzip < $BACKUP_DIR/full_${DATE}.sql.gz | \
mysql -u$MYSQL_USER -p$MYSQL_PASS verify_db
ROW_COUNT=$(mysql -u$MYSQL_USER -p$MYSQL_PASS \
-N -e "SELECT COUNT(*) FROM verify_db.users;")
echo "[$DATE] 验证 users 行数: $ROW_COUNT" >> $LOG
# 6. 清理验证库与过期备份
mysql -u$MYSQL_USER -p$MYSQL_PASS -e "DROP DATABASE verify_db;"
find $BACKUP_DIR -name "full_*.sql.gz" -mtime +7 -delete
echo "=== $DATE 备份恢复流程结束 ===" >> $LOG
设置 crontab 每日凌晨 2 点执行:
0 2 * * * /usr/local/bin/full_backup_restore.sh
❓ 常见问题
--single-transaction 参数不会锁表,利用 MVCC 一致性快照读取。MyISAM 表会加读锁,建议在低峰期备份或改用 XtraBackup。mysqlcheck --check 检查恢复后的表。至少每月验证一次。secure_file_priv 限制了 OUTFILE 路径怎么办?secure_file_priv 设置为指定目录(如 /var/lib/mysql-files/),或使用 mysql -e "SELECT ..." 重定向到客户端文件,绕过服务器端限制。--throttle 参数可限制 I/O 速率,建议在低峰期执行或配置限速参数。📖 小节
- mysqldump 是最常用的逻辑备份工具,支持全量/单库/单表/结构/数据 5 种模式
- --single-transaction 参数确保 InnoDB 备份不锁表,是生产环境必备参数
- mysql < file.sql 和 SOURCE 两种方式恢复逻辑备份
- SELECT INTO OUTFILE / LOAD DATA INFILE 适合 CSV 格式的数据导入导出
- binlog 实现时间点恢复(PITR),可精确到秒级回滚误操作
- XtraBackup 物理备份速度快不锁表,适合大型数据库
- 备份策略需根据数据量级选择:小库每日全量、大库每周全量+每日增量
- 异地备份和定期恢复演练是保障备份有效性的关键环节
📝 作业
-
基础题(难度⭐):使用 mysqldump 备份一个数据库,然后在新的数据库中恢复,对比原库和恢复库的表数量与行数。
-
基础题(难度⭐):将一张表的数据用 SELECT INTO OUTFILE 导出为 CSV,再用 LOAD DATA INFILE 导入到另一张表,验证数据一致。
-
进阶题(难度⭐⭐):模拟误删表场景:先做全量备份,执行一些 INSERT 操作,然后 DROP TABLE,用 binlog 时间点恢复到 DROP 之前的状态。
-
进阶题(难度⭐⭐):编写 Shell 脚本,实现每日自动全量备份+gzip 压缩+保留 7 天+备份结果邮件通知。
-
挑战题(难度⭐⭐⭐):设计一套完整的备份策略方案:包含全量/增量/异地/演练四个维度,针对 200 GB 的生产数据库,写出具体工具选型、执行频率、恢复步骤和验证方法。