Featured image of post MySQL 备份恢复实战:mysqldump + ibd2sql + binlog2sql + cron

MySQL 备份恢复实战:mysqldump + ibd2sql + binlog2sql + cron

本文系统整理 MySQL 备份与恢复的全套方案:mysqldump 通用备份、ibd2sql 从 .ibd 文件直接抽数据、binlog2sql 从 binlog 反解 DML、定时任务与远程上传的 shell 脚本模板。

为什么写这篇:MySQL 备份有 4 个维度——全量逻辑备份(mysqldump)、物理文件直抽(ibd2sql)、增量恢复(binlog2sql)、生产定时任务编排。任何一种出故障都能从其他三种救回来,才算合格的灾备体系。

适用读者:需要为 MySQL 设计完整备份方案的运维 / DBA。

前置知识:会用 Linux shell、知道 cron 表达式、了解 MySQL 基础。

目录

  1. mysqldump:通用逻辑备份
  2. 快速导入导出:sql 文件在容器内流转
  3. ibd2sql:从 .ibd 文件直接抽数据
  4. binlog 与 binlog2sql:增量恢复
  5. 定时备份 + 远程上传脚本
  6. 恢复场景实战:从全量 + binlog 恢复某张表
  7. Docker / K8s 环境的备份特例

1. mysqldump:通用逻辑备份

1.1 核心命令模板

1
2
3
4
5
6
7
8
mysqldump \
  --quick \
  --events \
  --all-databases \
  --flush-logs \
  --delete-master-logs \
  --single-transaction \
  > /backup/all_${dumpdate}.sql
参数含义是否必须
--quick, -q一行一行从服务器取,不缓存到内存大表必须
--events, -E导出事件重要
--all-databases, -A导出全部数据库全量备份时用
--flush-logs导出前刷新 binlog重要
--delete-master-logs主库备份后清理已备份的 binlog减少磁盘占用
--single-transaction用一致快照导出(仅 InnoDB)重要

关于 --flush-logs--delete-master-logs:使用 --all-databases 时,会逐个数据库刷新日志——但只有配合 --lock-all-tables--master-data 时所有表会同时锁定。要保证"导出 + 日志刷新"原子性,请同时加 --master-data--lock-all-tables

1.2 排除大表 / 只导结构 / 只导数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 排除表
docker exec mysql /usr/bin/mysqldump \
  -u root -p'{{REDACTED}}' \
  industry_iot_dev \
  --ignore-table=industry_iot_dev.location_historydata \
  --ignore-table=industry_iot_dev.base_log \
  > /backup/safety-20230904.sql

# 只导数据(不带 CREATE TABLE)
mysqldump -t -u root -p'{{REDACTED}}' industry_iot_dev --tables test_sync > test_sync.sql

# 只导结构(不带 INSERT)
mysqldump -d -u root -p'{{REDACTED}}' industry_iot_dev > schema.sql

1.3 Docker 容器内备份

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 在容器内 mysqldump 出来
docker exec mysql /usr/bin/mysqldump \
  -u root -p'{{REDACTED}}' \
  --single-transaction --quick \
  industry_iot_dev \
  --ignore-table=industry_iot_dev.location_historydata \
  > /home/docker/mysql/bak/safety-20231212.sql

# 再用 docker cp 把文件拷出来
docker cp mysql:/tmp/safety-20231212.sql /home/docker/mysql/bak/

# 或直接管道(容器内)
docker exec mysql bash -c "mysqldump -uroot -p'{{REDACTED}}' industry_iot_dev" > backup.sql

坑点:mysqldump 默认会写锁。如果容器跑的是生产库,必须加 --single-transaction,否则会阻塞业务写入。


2. 快速导入导出:sql 文件在容器内流转

2.1 标准做法(推荐)

1
2
3
4
5
# 1. 把 sql 文件复制进容器
docker cp backup.sql mysql:/tmp/backup.sql

# 2. 容器内执行导入
docker exec -it mysql bash -c "mysql -uroot -p'{{REDACTED}}' industry_iot_dev < /tmp/backup.sql"

2.2 关 binlog + 事务加速大文件导入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- 关闭 binlog(导入期间不写 binlog,提速 30%)
SET sql_log_bin = OFF;
-- 关闭自动提交
SET autocommit = 0;

USE industry_iot_dev;
START TRANSACTION;
SOURCE /tmp/backup.sql;
COMMIT;

-- 恢复
SET sql_log_bin = ON;
SET autocommit = 1;

SET sql_log_bin = OFF 必须是超级用户才有权限,且主从架构下从库别这么干——会导致从库 binlog 缺失,主从不一致。

2.3 Navicat 数据传输

开发机/测试机之间最省事的备份恢复是 Navicat 的"数据传输"功能:

  • 工具 → 数据传输 → 选源库 + 目标库 → 全勾选项(结构 + 数据)
  • 优点:自带进度条、可中断、跨 MySQL 版本兼容

坑点:表多时会卡死——Navicat 单连接执行所有 DDL。改成 1 张表 1 次传输


3. ibd2sql:从 .ibd 文件直接抽数据

ibd2sql 是国产开源工具(github.com/ddcw/ibd2sql),不需要 MySQL 实例,直接从 .ibd 文件里读 InnoDB 索引页、解析出行记录。

3.1 适用场景

场景是否适用
MySQL 实例起不来(磁盘损坏、配置错误)✓ 唯一能救命的办法
不锁表地查历史数据✓ 不会动原文件
审计某张表的删除记录✓ 配合 .ibd 副本可读
普通日常备份✗ 不如 mysqldump 简单

3.2 安装与使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 安装 Python3(一般系统都有)
apt install -y python3

# 下载最新版本
wget https://github.com/ddcw/ibd2sql/archive/refs/tags/v1.10.tar.gz
tar -xzf v1.10.tar.gz
cd ibd2sql-1.10

# 直接抽数据
python3 main.py /var/lib/mysql/your_db/your_table.ibd \
  --sql \
  --output /tmp/your_table.sql

默认会输出 INSERT 语句,直接 mysql 客户端执行即可恢复。


4. binlog 与 binlog2sql:增量恢复

4.1 什么是 binlog

binlog(二进制日志)记录了所有修改数据的 SQL,主要用于主从复制增量恢复

1
2
3
4
# 查看 binlog
docker exec -it mysql bash
cd /var/lib/mysql
mysqlbinlog --base64-output=DECODE-ROWS -v binlog.000013

4.2 反解 binlog 找某张表的变更

1
2
3
4
5
6
# 配合 grep 找到某张表的所有 DML
mysqlbinlog /var/lib/mysql/bin.000003 \
  --database industry_iot \
  --base64-output=decode-rows -vv \
  --skip-gtids=true \
  | grep -C 1 -i "risk_task_ctrl" > sql.log

4.3 binlog2sql:把 binlog 反解成可执行 SQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
pip install binlog2sql

# 关键场景:误删一张表后的精准恢复
python binlog2sql.py \
  -h127.0.0.1 -P3306 -uroot -p'{{REDACTED}}' \
  -d industry_iot \
  -t risk_task_ctrl \
  --start-file='binlog.000003' \
  --start-datetime='2016-12-13 20:25:00' \
  --stop-datetime='2016-12-13 20:30:00' \
  > recovery.sql
参数含义
--start-file从哪个 binlog 文件开始
--start-datetime起始时间(精确到秒)
--stop-datetime结束时间
-t指定表(不加则整个库)
-d指定库

典型误删恢复流程

  1. 立即发现误操作 → 不要慌,先看 SHOW MASTER STATUS 记录当前 binlog 位置
  2. 锁表:FLUSH TABLES WITH READ LOCK;(防止新写入覆盖现场)
  3. binlog2sql 生成反向 SQL(默认生成的就是回滚 SQL)
  4. 应用回滚 SQL
  5. 解锁:UNLOCK TABLES;

4.4 binlog 模式与 ROW vs STATEMENT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 查看 binlog 模式
SHOW VARIABLES LIKE 'binlog_format';
-- ROW       推荐(基于行的变更,主从一致性好)
-- STATEMENT 默认(基于 SQL 文本,主从不一致风险大)
-- MIXED     混合

-- 推荐改成 ROW
SET GLOBAL binlog_format = 'ROW';

-- 同时开启 binlog_rows_query_log_events(把原始 SQL 也记到 binlog 里)
SET GLOBAL binlog_rows_query_log_events = ON;

ROW 模式 + binlog_rows_query_log_events = ON 让你既能看到行级变化,又能直接看到原 SQL——调 bug 必备

4.5 binlog 重置

1
2
# 慎用!会清空所有 binlog
mysql -uroot -p'{{REDACTED}}' -e "RESET MASTER;"

5. 定时备份 + 远程上传脚本

5.1 cron 表达式速查

1
2
3
4
5
6
7
*    *    *    *    *
-    -    -    -    -
|    |    |    |    +----- 星期中星期几 (0 - 6) (星期天 为0)
|    |    |    +---------- 月份 (1 - 12)
|    |    +--------------- 一个月中的第几天 (1 - 31)
|    +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)

5.2 完整备份脚本(保留 7 天 + 上传 FTP)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env bash
# safety-local-mysqlbak.sh
set -e

DATE=$(date +%Y%m%d%H%M%S)
BACK_DATA=safety-${DATE}.sql
BAK_PATH=/home/docker/mysql/bak

if [ ! -d ${BAK_PATH} ]; then
  mkdir -p ${BAK_PATH}
fi

# 1. 备份
docker exec mysql /usr/bin/mysqldump \
  -u root -p'{{REDACTED}}' \
  --single-transaction --quick \
  industry_iot_dev \
  --ignore-table=industry_iot_dev.location_historydata \
  > ${BAK_PATH}/${BACK_DATA}

# 2. 删除 7 天前的
find ${BAK_PATH} -name "*.sql" -mtime +7 | xargs rm -rf

# 3. 保留最新副本软链
cp ${BACK_DATA} ${BAK_PATH}/safety_latest.sql

# 4. 上传 FTP(内网示例)
curl -u www:'{{REDACTED}}' \
  -T ${BAK_PATH}/safety_latest.sql \
  ftp://internal.example.com/tools/

# 5. 远程异地备份(公司机房示例)
curl -u www:'{{REDACTED}}' \
  -T /home/docker/mysql/bak/safety-${DATE}.sql \
  ftp://internal.example.com:25421/tools/

5.3 注册到 crontab

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
chmod +x /home/docker/mysql/backupmysql.sh

# 编辑定时任务
crontab -e

# 每天凌晨 0 点执行
0 0 * * * /home/docker/mysql/backupmysql.sh

# 或从远程拉脚本执行(参数:保留天数)
0 0 * * * /usr/bin/curl http://internal.example.com:9091/file/sh/safety-local-mysqlbak.sh | bash -s 7

参数化 cron:用 (crontab -l | grep -v "$cron_job"; echo "$cron_job") | crontab - 这种"过滤+追加"模式可以幂等地更新定时任务,不会重复添加。


6. 恢复场景实战:从全量 + binlog 恢复某张表

场景:某天发现 risk_task 表在 14:00 误删了某些行,备份是凌晨 3:00 的全量。

恢复步骤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 1. 找到 14:00 前最近的 binlog 位置
mysqlbinlog /var/lib/mysql/bin.000003 --base64-output=decode-rows -vv \
  --skip-gtids=true | grep -C 3 "DELETE FROM risk_task"

# 2. 生成风险点 03:00 ~ 14:00 之间 risk_task 表的完整变更
python binlog2sql.py \
  -uroot -p'{{REDACTED}}' \
  -d industry_iot -t risk_task \
  --start-file='binlog.000003' \
  --start-datetime='2024-06-15 03:00:00' \
  --stop-datetime='2024-06-15 14:00:00' \
  --sql \
  > /tmp/risk_task_recovery.sql

# 3. 先在测试库跑一遍
mysql -uroot -p'{{REDACTED}}' test_industry_iot < /tmp/risk_task_recovery.sql

# 4. 在生产库上找回 03:00 后被误删的数据
# (一般只取 DELETE 的反向 SQL,过滤掉 UPDATE)

前提:你的 binlog 一定要是 ROW 模式 + binlog_rows_query_log_events = ON + binlog 文件没被清理。


7. Docker / K8s 环境的备份特例

7.1 Docker 容器备份

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 备份(容器内)
docker exec mysql /usr/bin/mysqldump \
  -u root -p'{{REDACTED}}' \
  --single-transaction --skip-lock-tables --quick \
  industry_iot_dev \
  > /home/iot.sql

# 重建数据库
cat << "EOF" | docker exec -i mysql /usr/bin/mysql -u root --password='{{REDACTED}}'
DROP DATABASE IF EXISTS `industry_iot_dev`;
CREATE DATABASE `industry_iot_dev` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
EOF

# 导入
cat /home/iot.sql | docker exec -i mysql /usr/bin/mysql \
  -u root --password='{{REDACTED}}' industry_iot_dev

7.2 K8s 备份

1
2
3
4
5
6
7
8
9
# 通过 kubectl exec 在 Pod 内执行
kubectl exec -i "$(kubectl get pods -n mydb | grep mysql | awk '{print $1}')" \
  -n mydb \
  -- bash -c "/usr/bin/mysqldump --defaults-file=/etc/mysql/conf.d/my.cnf industry_iot_dev" \
  > safety.sql

# 传到 MinIO 备份
mc rm --versions --force minio-server/file/dev/safety/safety.sql
mc cp /file/safety.sql minio-server/file/dev/safety/

7.3 K8s 单表导入

1
2
cat risk_task.sql | kubectl exec -i $(kubectl get pods -n mydb | grep mysql | awk '{print $1}') \
  -n mydb -- bash -c "/usr/bin/mysql -u root --password='{{REDACTED}}' test"

7.4 导入时报 ASCII ‘\0’ 错误

1
ERROR: ASCII '\0' appeared in the statement

mysqldump 默认不识别二进制 0 字符。--binary-mode=1

1
2
3
4
5
cat /home/docker/mysql/bak/safety-20240504080004.sql | \
  docker exec -i mysql /usr/bin/mysql \
    --binary-mode=1 \
    -u root --password='{{REDACTED}}' \
    industry_iot_dev

经验总结

备份类型优点缺点适用
mysqldump 逻辑备份跨版本兼容、可读、可编辑大库慢、占内存中小库 / 跨版本迁移
XtraBackup 物理热备速度快、增量配置复杂大库 / 高频备份
binlog 增量时间点恢复依赖全量备份误删救命
ibd2sql 直抽 .ibd实例起不来也能读工具年轻灾难恢复

铁律只做一种备份 = 没有备份。生产环境至少 2 种备份组合(如:每天全量 + 持续 binlog + 每周异地同步)。


下一步

  • 数据同步 Canal / Debezium:见《MySQL 实时同步实战》
  • 索引与 SQL 优化:见《MySQL 索引优化实战》
  • 权限与安全:见《MySQL 用户权限与安全实战》

2024+ 视角:MySQL Shell + MySQL Operator + PIT 恢复

MySQL Shell(mysqlsh)—— 推荐替代 mysql/mysqldump

mysqlsh 是 8.0.13+ 引入的多语言客户端 + 工具集

1
2
3
4
5
6
7
8
# 启动 mysqlsh(默认 SQL 模式)
mysqlsh --uri=user@10.0.0.10:3306

# 切到 JS 模式
mysqlsh --uri=user@10.0.0.10:3306 --js

# 切到 Python 模式
mysqlsh --uri=user@10.0.0.10:3306 --py

核心价值:内置 util 模块提供并行 + 一致性的备份恢复能力。

util.dumpInstance() 替代 mysqldump

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// mysqlsh JS 模式
util.dumpInstance("/backup/full_dump", {
    threads: 8,                        // 并行 8 线程
    compression: "zstd",               // zstd 压缩(比 gzip 快 3 倍)
    chunking: true,                    // 分块大表
    showProgress: true,
    consistent: true,                  // FLUSH TABLES WITH READ LOCK
    ddlOnly: false,
    dataOnly: false
});

优势

  • 8 线程并行导出,比 mysqldump 快 5-10 倍
  • 自动分块大表(>50GB 也能稳定)
  • 断点续传:导出失败可 --resumeFrom 续传
  • 支持 zstd 压缩(比 gzip 快 3 倍、压缩比高 10%

util.loadDump() 导入

1
2
3
4
5
6
7
8
9
util.loadDump("/backup/full_dump", {
    threads: 8,
    showProgress: true,
    ignoreVersion: true,              // 跨大版本恢复
    loadUsers: true,                  // 同时导入用户
    loadDdl: true,
    loadData: true,
    skipBinlog: true                  // 从库导入时跳过 binlog
});

MySQL Operator for Kubernetes(2024+ GA)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# mysql-innodbcluster.yaml
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: my-cluster
spec:
  instances: 3
  baseServerId: 1000
  tls:
    useSelfSigned: true
  router:
    instances: 2
  secretName: mysql-cluster-secret

优势

  • 3 节点 InnoDB Cluster 自动部署(Group Replication)
  • MySQL Router 自动 sidecar
  • 滚动升级、备份、监控一应俱全
  • 对标 Percona Operator for MySQL

2024+ 备份恢复工具矩阵

工具特点2024+ 适用
mysqldump老牌、单线程小库 / 跨版本兼容
mysql-shell util并行、zstd、断点续传生产首选
mydumper / myloaderC 写的并行工具大库快速备份
Percona XtraBackup 8.x物理热备TB 级库 + 快速恢复
MySQL Enterprise Backup (MEB)商业企业版特性
TiDB DM / OceanBase OMS异构同步MySQL → TiDB / OB

PIT(Point-In-Time)恢复的标准流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 1. 全量备份
mysqlsh --uri=user@db-host -e "util.dumpInstance('/backup/2024-06-15-0300')"

# 2. 恢复全量到临时库
mysqlsh --uri=user@temp-host -e "util.loadDump('/backup/2024-06-15-0300')"

# 3. 解析 binlog 到 PIT
mysqlbinlog --start-datetime='2024-06-15 03:00:00' \
            --stop-datetime='2024-06-15 14:00:00' \
            --read-from-remote-server \
            --host=db-host --user=repl \
            binlog.000003 binlog.000004 \
            > /tmp/pit.sql

# 4. 应用到临时库
mysql -u root -p temp_db < /tmp/pit.sql

# 5. 切换业务(DNS / 负载均衡切到 temp_db)

Cloud-Native 备份新方案

  • AWS RDS/Aurora:自动备份 + PIT 恢复(5 分钟内任意点)
  • 阿里云 RDS:跨地域备份 + 备份集查询
  • 腾讯云 CynosDB:binlog 实时上传 COS,30 天可回放

2024+ 安全加固

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- 8.0+ 强制 TLS 连接
ALTER USER 'app_user'@'%' REQUIRE SSL;

-- 8.0+ 密码强度策略
SET PERSIST validate_password.policy = STRONG;
SET PERSIST validate_password.length = 12;

-- 8.0+ 双因素认证
ALTER USER 'admin'@'%' ADD FACTOR 2;

-- 8.0.27+ 审计日志
SET PERSIST audit_log_format = JSON;
SET PERSIST audit_log_policy = ALL;

实战坑(2024+)

  • mysqlsh util.dumpInstance 默认不导出 mysql.user,要用 --users flag
  • **跨大版本恢复(5.7 → 8.4)**必须 mysql_upgrade,且要先检查是否有保留字冲突
  • MySQL Operator 2.x 部署后默认开启 MySQL Router sidecar——K8s service 指向 router 而非直接指向 MySQL Pod
  • MySQL HeatWave(Oracle 云)支持"自动 binlog 解析 + 一致性快照导出"——但仅限 Oracle Cloud
使用 Hugo 构建
主题 StackJimmy 设计