MySQL的并行复制原理是什么?
MySQL的并行复制是解决主从延迟问题的关键技术演进。它的核心思想是:将主库上并发执行的事务,在从库上也用多线程并发重放,而不是传统的单线程顺序执行。
我将从 演进历程、核心原理、版本对比和生产实践 四个维度,为您深入解析。
一、 演进历程与问题背景
1.1 单线程复制时代(MySQL 5.6之前)
-- 从库上只有一个SQL线程,串行执行relay log
主库:事务A → 事务B → 事务C → 事务D (并发执行)
从库:事务A → 事务B → 事务C → 事务D (串行执行)
问题:主库多核并发写入,从库单核重放,必然产生主从延迟。
1.2 并行复制演进路线
MySQL 5.6:基于数据库的并行(DATABASE策略)
↓
MySQL 5.7:基于逻辑时钟的并行(LOGICAL_CLOCK策略)
↓
MySQL 8.0:基于WriteSet的并行(WRITESET策略)★ 当前最优
二、 三种并行复制原理详解
2.1 MySQL 5.6:基于DATABASE的并行
原理:按数据库名进行并行,不同数据库的事务可以并发执行。
-- 配置
slave_parallel_workers = 4
slave_parallel_type = DATABASE
-- 示例:不同数据库的事务可以并行
事务1:UPDATE db1.users SET ... -- 线程1执行
事务2:UPDATE db2.orders SET ... -- 线程2执行(可并行)
事务3:UPDATE db1.products SET ... -- 线程1执行(与事务1串行,同库)
实现机制:
- 协调线程(Coordinator)从relay log读取事务
- 按数据库名哈希分配到不同的工作线程
- 同数据库的事务保持顺序,不同数据库的事务可以并行
局限性:
- 单数据库应用无法受益
- 跨库事务可能引起冲突
2.2 MySQL 5.7:基于LOGICAL_CLOCK的并行(重大突破)
核心原理:按事务组并行,同一组内事务可以并行执行。
2.2.1 关键概念
- 逻辑时钟(Logical Clock):记录事务提交的顺序时间戳
- last_committed:事务组的起始逻辑时钟
- sequence_number:事务的结束逻辑时钟
2.2.2 Binlog格式变化
-- 5.7的binlog中新增了并行信息
# original_commit_timestamp=1625097600000000
# immediate_commit_timestamp=1625097600005000
/* last_committed=100 sequence_number=101 */
-- 事务A:last_committed=100, sequence_number=101
/* last_committed=100 sequence_number=102 */
-- 事务B:last_committed=100, sequence_number=102 (可与A并行)
/* last_committed=102 sequence_number=103 */
-- 事务C:last_committed=102, sequence_number=103 (必须等A/B提交后)
2.2.3 并行判断逻辑
# 伪代码:协调线程的分配逻辑
def can_parallel_transaction(tx1, tx2):
# last_committed相同的事务可以并行执行
if tx1.last_committed == tx2.last_committed:
return True # 可以并行
else:
return False # 必须串行,tx2要等tx1提交
2.2.4 主库的并行控制
-- 主库配置,控制事务组大小
binlog_group_commit_sync_delay = 1000 # 微秒,延迟提交以组更大事务组
binlog_group_commit_sync_no_delay_count = 10 # 达到N个事务立即提交
-- 事务提交过程:
1. 准备阶段(Flush Stage):事务写入binlog缓存
2. 同步阶段(Sync Stage):多个事务一起fsync到磁盘
3. 提交阶段(Commit Stage):按组顺序标记last_committed
2.3 MySQL 8.0:基于WRITESET的并行(当前最优)
原理革命:直接分析事务修改的数据行,无冲突的事务即可并行。
2.3.1 WriteSet核心机制
// 概念:每个事务修改的数据行集合
WriteSet = {
"db1.table1:primary_key=100", // 修改的行1
"db1.table2:primary_key=200" // 修改的行2
}
// 并行规则:WriteSet无交集的事务可以并行
事务A WriteSet: {row1, row2}
事务B WriteSet: {row3, row4} // 与A无交集,可以并行
事务C WriteSet: {row2, row5} // 与A有交集row2,必须等A提交
2.3.2 配置与启用
-- 主库配置
transaction_write_set_extraction = XXHASH64 -- 计算行哈希的算法
binlog_transaction_dependency_tracking = WRITESET -- ★ 关键配置
binlog_transaction_dependency_history_size = 25000 -- 历史记录大小
-- 从库配置
slave_parallel_workers = 8 -- 并行线程数
slave_parallel_type = LOGICAL_CLOCK -- 仍用此类型,但实际用WRITESET判断
2.3.3 WriteSet的智能优化
-- 示例:即使不同时间提交的事务,只要无冲突也可并行
事务A(10:00): UPDATE users SET name='Alice' WHERE id=1
事务B(10:01): UPDATE orders SET amount=100 WHERE user_id=2
-- 修改不同用户的数据,无冲突,可以并行回放
-- WriteSet还会识别外键依赖、唯一约束冲突
-- 自动处理:事务修改了外键关联行,关联事务会被正确排序
三、 三种策略的对比分析
| 特性维度 | DATABASE策略 (5.6) | LOGICAL_CLOCK策略 (5.7) | WRITESET策略 (8.0) |
|---|---|---|---|
| 并行粒度 | 数据库级别 | 事务组级别 | 数据行级别 |
| 并行度 | 低(受数据库数量限制) | 中(受事务组大小限制) | 高(仅受实际冲突限制) |
| 配置复杂度 | 简单 | 中等 | 中等 |
| 适用场景 | 多数据库应用 | 通用场景 | 高并发OLTP |
| 主从延迟 | 降低有限 | 显著降低 | 最大程度降低 |
| 数据一致性 | 强 | 强 | 强 |
四、 生产环境配置与监控
4.1 推荐配置(MySQL 8.0)
-- ★ 主库配置 my.cnf ★
[mysqld]
server_id = 1
log_bin = mysql-bin
binlog_format = ROW -- 必须使用ROW格式
# 并行复制关键配置
binlog_transaction_dependency_tracking = WRITESET
transaction_write_set_extraction = XXHASH64
binlog_transaction_dependency_history_size = 25000
# 控制事务组大小(根据业务调整)
binlog_group_commit_sync_delay = 1000 -- 微秒,适当增加以增大组
binlog_group_commit_sync_no_delay_count = 100 -- 事务数阈值
-- ★ 从库配置 my.cnf ★
[mysqld]
server_id = 2
relay_log = mysql-relay-bin
# 并行复制配置
slave_parallel_workers = 8 -- CPU核心数的50-75%
slave_parallel_type = LOGICAL_CLOCK -- 使用WRITESET时需要设为这个
slave_preserve_commit_order = ON -- ★ 重要:保持提交顺序,确保一致性
# 性能优化
slave_pending_jobs_size_max = 2G -- 增大工作队列
4.2 监控并行复制状态
-- 查看复制线程状态
SHOW PROCESSLIST;
/*
+----+-------------+---------------------+------+---------+------+---------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+---------------------+------+---------+------+---------------------------------------------------------------+------------------+
| 5 | system user | | NULL | Connect | 1000 | Waiting for an event from Coordinator | NULL |
| 6 | system user | | NULL | Connect | 100 | Waiting for prior transaction to commit | NULL |
| 7 | system user | | NULL | Connect | 50 | updating | UPDATE users... |
+----+-------------+---------------------+------+---------+------+---------------------------------------------------------------+------------------+
*/
-- 查看并行复制统计信息(MySQL 8.0+)
SELECT * FROM performance_schema.replication_applier_status_by_worker;
/*
+-----------+-----------+---------------+-------------------+-----------------------+----------------------+
| CHANNEL | WORKER_ID | THREAD_ID | SERVICE_STATE | LAST_ERROR_NUMBER | LAST_APPLIED_TRANSACTION |
+-----------+-----------+---------------+-------------------+-----------------------+----------------------+
| channel_1 | 1 | 50 | ON | 0 | ANONYMOUS |
| channel_1 | 2 | 51 | ON | 0 | ANONYMOUS |
+-----------+-----------+---------------+-------------------+-----------------------+----------------------+
*/
-- 关键监控指标
SHOW STATUS LIKE 'Slave_parallel%';
/*
+----------------------------------------+-------+
| Variable_name | Value |
+----------------------------------------+-------+
| Slave_parallel_workers | 8 |
| Slave_parallel_workers_created | 8 | -- 已创建的工作线程数
| Slave_last_queued_transaction_retries | 0 | -- 重试次数,>0可能有冲突
+----------------------------------------+-------+
*/
4.3 检测主从延迟与并行效果
-- 查看当前复制位置和延迟
SHOW SLAVE STATUS\G
-- 关键字段:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0 -- 主从延迟秒数
-- Retrieved_Gtid_Set:
-- Executed_Gtid_Set:
-- 更详细的并行复制监控
SELECT
WORKER_ID,
COUNT(*) as transactions_applied,
SUM(TIMER_END - TIMER_START)/1000000000 as total_time_sec,
AVG(TIMER_END - TIMER_START)/1000000000 as avg_time_sec
FROM performance_schema.events_transactions_summary_by_thread_by_event_name
WHERE THREAD_ID IN (
SELECT THREAD_ID
FROM performance_schema.replication_applier_status_by_worker
)
GROUP BY WORKER_ID;
4.4 故障排查与调优
-- 1. 如果并行度不高,检查WriteSet配置
SHOW VARIABLES LIKE '%dependency%';
SHOW VARIABLES LIKE '%write_set%';
-- 2. 检查是否有热点数据导致冲突
-- 通过performance_schema分析事务冲突
SELECT
EVENT_NAME,
COUNT(*) as conflict_count
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE '%write/set%'
GROUP BY EVENT_NAME;
-- 3. 调整工作线程数(动态调整,无需重启)
STOP SLAVE SQL_THREAD;
SET GLOBAL slave_parallel_workers = 16; -- 根据CPU核心数调整
START SLAVE SQL_THREAD;
-- 4. 监控工作线程负载是否均衡
-- 如果不均衡,可能某些线程总是处理热点数据
五、 生产实践建议
5.1 版本选择策略
- 新项目:直接使用 MySQL 8.0 + WRITESET策略
- 升级项目:从5.6/5.7升级到8.0,并行复制性能会有质的提升
- 兼容性考虑:如果从库版本低于主库,需确认并行复制特性兼容
5.2 配置黄金法则
# 工作线程数设置(经验公式)
slave_parallel_workers = min(CPU核心数 × 0.75, 32)
# 内存配置调整
slave_pending_jobs_size_max = 可用内存 × 0.1 # 但不超过4G
# 对于WriteSet策略
binlog_transaction_dependency_history_size = 25000 # 默认值通常足够
5.3 特定场景优化
-- 场景1:批量导入数据时的优化
-- 临时关闭并行复制,避免大量事务冲突
STOP SLAVE;
SET GLOBAL slave_parallel_workers = 0;
-- 执行数据导入
START SLAVE;
-- 场景2:读写分离架构
-- 从库配置更强的硬件(更多CPU核心)
-- 因为从库需要并行重放主库所有写操作
-- 场景3:多源复制
-- 每个复制通道独立配置并行参数
CHANGE MASTER TO
MASTER_HOST='source1',
SLAVE_PARALLEL_WORKERS=4
FOR CHANNEL 'source1';
CHANGE MASTER TO
MASTER_HOST='source2',
SLAVE_PARALLEL_WORKERS=4
FOR CHANNEL 'source2';
5.4 注意事项
- 必须使用ROW格式binlog:并行复制依赖行级别的变更信息
- 保持事务大小适中:过大事务会限制并行度
- 监控热点数据:频繁更新的同一行数据会成为并行瓶颈
- 测试环境验证:上线前务必在测试环境验证并行复制配置
总结
MySQL并行复制的演进体现了数据库技术对高并发、低延迟需求的不懈追求:
- DATABASE策略:解决了多数据库应用的基础并行需求
- LOGICAL_CLOCK策略:实现了事务组级别的智能并行,是重大突破
- WRITESET策略:通过数据行冲突检测实现近乎最优的并行,代表了当前最高水平
作为架构师,关键要理解:
- 并行复制的本质是 "在主库并发的事务,尽量在从库也并发重放"
- WriteSet策略的先进性在于 "只对有真正数据冲突的事务进行串行化"
- 配置的黄金法则是 "足够的工作线程 + 正确的冲突检测策略"
在实际生产中,MySQL 8.0的WRITESET策略配合适当的线程数和内存配置,能将主从延迟控制在毫秒级,满足绝大多数高并发场景的需求。这背后的核心哲学是:通过精细化的冲突检测,最大化并行度,同时严格保证数据一致性。