前言
在 MySQL 备份实践中,mysqldump --single-transaction --master-data=2 是最常见的组合。但很多 DBA 只知道"这样用不锁表",却不清楚底层原理。今天我们深入剖析这两个参数的工作机制,以及为什么它们必须配合使用。
一、MVCC 可见性规则(理论基础)
在理解 --single-transaction 之前,必须先掌握 InnoDB 的 MVCC 机制。
1.1 Read View 结构
当事务执行 START TRANSACTION WITH CONSISTENT SNAPSHOT 时,InnoDB 会创建一个 Read View:
| |
注意:InnoDB 的命名反直觉
m_low_limit_id虽然叫 “low limit”,但实际是最大值(下一个要分配的事务ID)m_up_limit_id虽然叫 “up limit”,但实际是最小值(最小活跃事务ID)
1.2 可见性判断规则
| |
可见性判断示例:
假设创建 Read View 时:
- 已提交事务:trx_1 到 trx_99
- 活跃事务:trx_100, trx_102, trx_105
- 下一个事务ID:trx_106
则 Read View 为:
| |
判断各行可见性:
row_trx_id = 95:95 < 100 → 可见(规则2,Read View 创建前已提交)row_trx_id = 100:100 在 m_ids 中 → 不可见(规则4,未提交)row_trx_id = 101:101 不在 m_ids 中 → 可见(规则4,已提交)row_trx_id = 107:107 >= 106 → 不可见(规则3,Read View 创建后才开始)
1.3 Undo Log 回滚链
如果当前版本不可见,InnoDB 通过 roll_ptr 指针回溯历史版本:
当前版本 (trx_id=107, 不可见)
↓ roll_ptr
历史版本 (trx_id=101, 可见) ← mysqldump 读取这个版本
↓ roll_ptr
更早版本 (trx_id=95)
Undo Log 记录结构:
Undo Log Entry {
trx_id: 107
undo_type: UPDATE
table_id: 123
old_values: {id: 1, name: "旧值"}
roll_ptr: 指向更早的 undo log
}
二、--single-transaction 参数详解
2.1 功能定位
作用:利用 MVCC 创建一致性快照,导出过程不锁表
底层执行:
| |
2.2 工作原理
时间线示例:
T0: 数据库正常运行
- users 表有 100 条记录
- 已提交事务:trx_1 到 trx_99
- 活跃事务:trx_100, trx_102
T1: mysqldump 执行 START TRANSACTION WITH CONSISTENT SNAPSHOT
- 创建 Read View
- Read View = {
m_up_limit_id: 100, # 最小活跃事务ID
m_low_limit_id: 103, # 下一个要分配的ID
m_ids: [102, 100], # 活跃事务列表(降序)
m_creator_trx_id: 101 # mysqldump 的事务ID
}
T2: 其他事务提交并插入新数据
- trx_103: INSERT INTO users VALUES (101, '张三');
- trx_104: UPDATE users SET name='李四' WHERE id=1;
T3: mysqldump 导出数据
- 读取 users 表
- trx_103 的插入:trx_id=103 >= m_low_limit_id(103) → 不可见(规则3)
- trx_104 的更新:trx_id=104 >= m_low_limit_id(103) → 不可见(规则3)
- 导出结果:仍然是 100 条记录,id=1 的 name 是旧值
T4: mysqldump 完成
- COMMIT
2.3 关键特性
✅ 优点:
- 不锁表,业务不受影响
- 数据一致性:导出的是 T1 时刻的完整快照
❌ 缺点:
- 只适用于 InnoDB 表(MyISAM 不支持 MVCC)
- 不知道快照对应哪个 binlog 位置
三、--master-data 参数详解
3.1 功能定位
作用:记录备份时刻对应的 binlog 位置,用于搭建主从复制
参数值:
--master-data=1:在 dump 文件中写入CHANGE MASTER TO语句--master-data=2:写入但注释掉(推荐,用于参考)
3.2 底层执行流程
| |
3.3 FTWRL 的作用
FLUSH TABLES WITH READ LOCK 做了什么?
- 阻止所有写操作:INSERT/UPDATE/DELETE/DDL 全部被阻塞
- 等待现有事务提交:确保所有未提交事务完成
- 创建一个"静止点":此时没有任何事务在执行
时间窗口:
T1: FTWRL 执行 → 等待 trx_100, trx_102 提交
T2: 所有事务提交完成 → FTWRL 获取成功
T3: SHOW MASTER STATUS → 记录 binlog pos 456789
T4: START TRANSACTION → 创建 Read View
T5: UNLOCK TABLES → 释放锁(通常 < 1秒)
关键点:T2-T5 之间没有任何事务提交,保证 binlog 位置和 Read View 状态完全一致。
此时创建的 Read View:
| |
因为 FTWRL 等待了所有事务提交,所以 Read View 的活跃列表为空,这是一个"干净"的快照。
四、为什么必须配合使用
4.1 单独使用的问题
问题1:只用 --single-transaction
备份结果:
✅ 得到一致性快照
❌ 不知道对应哪个 binlog 位置
❌ 无法搭建主从复制
具体场景:
T1: mysqldump 创建 Read View(users 表 100 条记录)
T2: 其他事务插入 10 条记录,binlog 写到 pos 2000
T3: mysqldump 导出完成(dump 文件 100 条记录)
问题:从库应该从哪个 binlog 位置开始同步?
- 从 pos 2000?会丢失那 10 条记录
- 从更早的位置?可能重复应用已在 dump 中的数据
- 答案:不知道!❌
问题2:只用 --master-data
备份过程:
T1: FLUSH TABLES WITH READ LOCK
T2: SHOW MASTER STATUS
T3: 导出数据(持续持有全局锁)← 问题
T4: UNLOCK TABLES
问题:
❌ 导出期间所有写入被阻塞
❌ 如果数据库 100GB,导出需要 1 小时
❌ 业务停止 1 小时
4.2 核心问题:时间点对齐
目标公式:
dump 文件的数据 + binlog(从某位置开始) = 完整数据
必须保证:
dump 文件看到的数据状态 === binlog 位置记录的数据状态
4.3 错误场景分析
场景1:先记录 binlog,再创建快照
T1: SHOW MASTER STATUS → pos 1000
T2: 其他事务提交 → binlog 写到 pos 1500
T3: START TRANSACTION(Read View 包含 T2 的修改)
T4: 导出数据
结果:
- binlog pos 1000 不包含 T2
- dump 文件包含 T2
- 从库应用 binlog 时重复执行 T2 → 数据重复 ❌
具体例子:
| |
场景2:先创建快照,再记录 binlog
T1: START TRANSACTION(创建 Read View)
T2: 其他事务提交 → binlog 写到 pos 1500
T3: SHOW MASTER STATUS → pos 1500
T4: 导出数据(看到的是 T1 的快照,不包含 T2)
结果:
- dump 文件不包含 T2
- binlog pos 1500 包含 T2
- 从库应用 binlog 时跳过 T2 → 数据丢失 ❌
具体例子:
| |
4.4 正确流程:FTWRL 保证原子性
T1: FLUSH TABLES WITH READ LOCK
↓ 阻止写入,等待现有事务提交
T2: SHOW MASTER STATUS → pos 1500
↓ binlog 位置确定
T3: START TRANSACTION(创建 Read View)
↓ Read View 内容 === pos 1500 的状态
T4: UNLOCK TABLES
↓ 释放锁,业务恢复
T5: 导出数据(使用 T3 的 Read View)
关键:T2-T4 之间没有任何事务提交,binlog 位置和 Read View 状态完全一致。
五、深入理解 FTWRL
5.1 FTWRL 的等待机制
| |
风险场景:
T1: 用户执行 BEGIN; SELECT * FROM big_table; (未提交)
T2: mysqldump 执行 FTWRL → 等待 T1
T3: 所有其他写入被阻塞 → 生产故障
解决方案:
| |
5.2 FTWRL 的锁范围
| |
六、参数功能总结
6.1 --single-transaction 的职责
解决的问题:
- ✅ 不锁表:利用 MVCC,业务不受影响
- ✅ 一致性:导出的数据是某个时刻的完整快照
无法解决的问题:
- ❌ 不知道快照对应哪个 binlog 位置
- ❌ 无法用于搭建主从复制
6.2 --master-data 的职责
解决的问题:
- ✅ 可追溯:记录快照对应的 binlog 位置
- ✅ 可复制:从库可以从这个位置开始同步
无法解决的问题:
- ❌ 单独使用会长时间锁表
- ❌ 需要 FTWRL 保证位置和快照一致
6.3 配合使用的价值
核心公式:
dump 文件(快照) + binlog(从记录位置开始) = 完整数据
前提:快照时刻 === binlog 位置时刻
保证:FTWRL 的原子性操作
FTWRL 是桥梁:在短暂的全局锁期间,同时完成"记录位置"和"创建快照",保证两者对齐。
七、总结
7.1 核心要点
--single-transaction利用 MVCC 创建一致性快照,不锁表--master-data记录 binlog 位置,用于主从复制- FTWRL 是桥梁,保证 binlog 位置和 Read View 状态一致
- 两者必须配合,才能实现"不锁表 + 可复制"的完美备份
7.2 理解层次
初级 DBA:知道这样用不锁表 中级 DBA:理解 MVCC 快照和 binlog 位置 高级 DBA:掌握 Read View 可见性规则和 FTWRL 原子性保证
7.3 常见误区
❌ 误区1:认为 --single-transaction 完全不加锁
✅ 真相:FTWRL 会短暂加全局锁(通常 < 1秒)
❌ 误区2:认为可以先备份再记录 binlog 位置 ✅ 真相:必须在 FTWRL 保护下同时完成,否则数据不一致
❌ 误区3:认为 MyISAM 表也能用 --single-transaction
✅ 真相:MyISAM 不支持 MVCC,会退化为锁表备份
7.4 生产环境推荐命令
| |
作者:DBA 随记 日期:2026-03-29 标签:mysqldump, 备份恢复, MVCC, 主从复制