MySQL: MySQL 备份与恢复完全指南

最后更新:2026-08-26

Alice 的公司服务器硬盘突然损坏,数据库彻底崩溃。没有备份,3 年的客户数据、订单记录、财务信息永远丢失。这次灾难让她痛定思痛,建立了完善的备份体系:每日全量 mysqldump + binlog 持续增量 + 每周异地传输 + 每月恢复演练。从那以后,即便再次遭遇硬件故障,她也能在 30 分钟内完整恢复所有数据。

本课你将学到:

100%
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 TABLEINSERT 等语句。恢复时重新执行这些 SQL 即可重建数据。代表工具:mysqldump

优点: 可读性强、跨版本兼容、可选择性备份单库单表。

缺点: 速度慢、备份和恢复都需要 MySQL 进程参与、大库耗时长。

(2) 物理备份概述

物理备份直接复制数据库的底层数据文件(.ibd.frm 等),恢复时将文件复制回数据目录即可。代表工具:XtraBackup

优点: 速度快、不锁表(InnoDB)、适合大库。

缺点: 不可读、跨平台受限、需停机或使用专用工具保持一致性。

对比项 逻辑备份(mysqldump) 物理备份(XtraBackup)
备份内容 SQL 文本语句 数据文件副本
备份速度 慢(逐行导出) 快(文件复制)
恢复速度 慢(逐行执行 SQL) 快(文件复制回)
锁表影响 InnoDB 可免锁 不锁表
可读性 高(纯文本 SQL) 低(二进制文件)
存储开销 小(文本压缩率高) 大(完整文件副本)
跨版本 兼容 需版本匹配
适用场景 中小型库 ≤ 50 GB 大型库 > 50 GB

3. mysqldump 逻辑备份详解

(1) 全量备份

全量备份导出所有数据库的完整数据,是最基础也是最安全的备份方式。

BASH
mysqldump -u root -p --all-databases --single-transaction > full_backup.sql

--single-transaction 参数在 InnoDB 表上使用一致性快照读取,不会锁表。MyISAM 表仍会锁表。

(2) 单库与单表备份

BASH
# 备份单个数据库
mysqldump -u root -p --single-transaction mydb > mydb_backup.sql

# 备份指定表
mysqldump -u root -p --single-transaction mydb users orders > tables_backup.sql

(3) 结构与数据分离

BASH
# 只备份表结构
mysqldump -u root -p --no-data mydb > mydb_schema.sql

# 只备份数据
mysqldump -u root -p --no-create-info mydb > mydb_data.sql

▶ 示例:mysqldump 五种备份模式

BASH
# 模式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 文件恢复数据到指定数据库:

BASH
mysql -u root -p mydb < mydb_backup.sql

恢复压缩备份文件:

BASH
gunzip < mydb_backup.sql.gz | mysql -u root -p mydb

(2) SOURCE 命令恢复

在 MySQL 客户端内使用 SOURCE 命令恢复:

SQL
USE mydb;
SOURCE /var/backups/mysql/mydb_backup.sql;

▶ 示例:恢复流程

BASH
# 步骤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;"

▶ 示例:恢复压缩备份

BASH
# 恢复 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 文件:

SQL
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 文件导入数据到表:

SQL
LOAD DATA INFILE '/tmp/active_users.csv'
INTO TABLE users_copy
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;

▶ 示例:CSV 导出导入完整流程

SQL
-- 导出订单数据为 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 导入客户端文件

SQL
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)。

SQL
-- 查看 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 到目标时间点

BASH
# 步骤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

▶ 示例:误删表后的时间点恢复

TEXT 📖 仅展示
场景: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 表数据完整性
BASH
# 实际执行命令
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 开发的开源物理备份工具,核心原理:

  1. 后台线程 持续读取 InnoDB 数据页,同时监控 redo log 变化
  2. 备份期间新产生的 redo log 也被持续记录
  3. 备份结束后,用 redo log 前滚数据页到一致性状态
  4. 全程不锁表,不影响业务读写

(2) 基本操作

BASH
# 全量备份
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 全量备份与恢复

BASH
# 全量备份
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) 全量与增量组合

(2) 异地备份

备份文件必须传输到异地存储,防止机房级灾难:

BASH
# 使用 rsync 传输到远程
rsync -avz /backup/mysql/ backup-server:/data/mysql_backup/

# 使用 scp 传输
scp /backup/mysql/full_20260703.sql.gz backup-server:/data/backup/

(3) 定期恢复演练

备份不验证等于没有备份。至少每月进行一次完整的恢复演练,确认:

数据规模 全量频率 增量方式 异地策略 恢复演练
≤ 10 GB 每日全量 binlog 每日传输 每月一次
10-100 GB 每日全量 binlog 每日传输 每月一次
100 GB - 1 TB 每周全量 每日 XtraBackup 增量 每周传输 每季一次
> 1 TB 每周全量 每日 XtraBackup 增量 实时同步 每季一次
恢复方式 适用场景 恢复速度 操作复杂度 数据粒度
mysqldump 全量恢复 误删库/全库迁移 全库
binlog 时间点恢复 误操作回滚 秒级
XtraBackup 恢复 硬件故障/灾难 全库
LOAD DATA INFILE 单表数据补录 单表

9. 自动化备份与综合示例

(1) 自动化备份要点

▶ 示例:自动化备份脚本

BASH
#!/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

▶ 示例:完整备份恢复脚本

BASH
#!/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 点执行:

BASH
0 2 * * * /usr/local/bin/full_backup_restore.sh

❓ 常见问题

Q mysqldump 会锁表吗?
A InnoDB 表使用 --single-transaction 参数不会锁表,利用 MVCC 一致性快照读取。MyISAM 表会加读锁,建议在低峰期备份或改用 XtraBackup。
Q binlog 和 redo log 有什么区别?
A redo log 是 InnoDB 引擎层的日志,用于崩溃恢复,循环写入;binlog 是 MySQL Server 层的日志,用于主从复制和时间点恢复,追加写入且可归档。两者记录内容和用途完全不同。
Q 备份文件大概有多大?
A 逻辑备份约为原始数据的 30%-80%(文本格式),gzip 压缩后约 10%-30%。物理备份接近原始数据大小。建议预留 3 倍数据量的存储空间。
Q 如何验证备份是否可用?
A 定期在测试环境执行完整恢复,校验表数量、行数、关键业务数据。也可用 mysqlcheck --check 检查恢复后的表。至少每月验证一次。
Q 误删了表怎么恢复?
A 恢复最近全量备份到临时库,再用 mysqlbinlog 重放 binlog 到 DROP TABLE 前的时刻,最后从临时库导出被删表的数据回填到生产库。
Q secure_file_priv 限制了 OUTFILE 路径怎么办?
Asecure_file_priv 设置为指定目录(如 /var/lib/mysql-files/),或使用 mysql -e "SELECT ..." 重定向到客户端文件,绕过服务器端限制。
Q XtraBackup 备份期间会影响性能吗?
A 会有一定 I/O 开销,但 XtraBackup 提供 --throttle 参数可限制 I/O 速率,建议在低峰期执行或配置限速参数。

📖 小节


📝 作业

  1. 基础题(难度⭐):使用 mysqldump 备份一个数据库,然后在新的数据库中恢复,对比原库和恢复库的表数量与行数。

  2. 基础题(难度⭐):将一张表的数据用 SELECT INTO OUTFILE 导出为 CSV,再用 LOAD DATA INFILE 导入到另一张表,验证数据一致。

  3. 进阶题(难度⭐⭐):模拟误删表场景:先做全量备份,执行一些 INSERT 操作,然后 DROP TABLE,用 binlog 时间点恢复到 DROP 之前的状态。

  4. 进阶题(难度⭐⭐):编写 Shell 脚本,实现每日自动全量备份+gzip 压缩+保留 7 天+备份结果邮件通知。

  5. 挑战题(难度⭐⭐⭐):设计一套完整的备份策略方案:包含全量/增量/异地/演练四个维度,针对 200 GB 的生产数据库,写出具体工具选型、执行频率、恢复步骤和验证方法。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏