10人参与 • 2025-10-27 • Mysql
并行复制是 mysql 复制技术中一个至关重要的性能特性,它主要用于解决主从延迟(replication lag)问题。它的核心思想是:在从库上,使用多个工作线程来并发应用(apply)从主库接收到的二进制日志(binary log)事件,从而大幅提升从库的数据回放速度。
在传统的单线程复制模式下(sql 线程为单线程),从库的 sql 线程严格按照主库二进制日志(binlog)中事件的顺序,逐一地、串行地执行 sql 事件。
这就导致了:从库的应用速度远远跟不上主库的写入速度,数据延迟(seconds behind master)会越来越大。并行复制就是为了将这个“单车道”改造成“多车道”。
并行复制并非简单地将所有事件乱序并行执行,因为存在依赖关系的事务(例如,对同一行的修改)必须按顺序执行,否则会导致数据不一致。因此,并行复制的核心技术在于如何准确地识别出哪些事务之间没有依赖关系,可以安全地并行执行。
mysql 的并行复制技术经历了多个版本的演进,其算法也在不断优化:
缺点:粒度太粗。绝大多数应用通常只使用一个数据库,或者不同数据库的负载不均衡,导致无法有效并行。这对于现代微服务或多租户架构来说效果有限。
slave_parallel_workers = 4 -- 设置并行工作线程数 slave_parallel_type = database -- 并行策略为 database
这是里程碑式的改进,极大地提升了并行复制的效率和通用性。
策略:基于主库的组提交(group commit) 信息。
commit 操作打包成一个组(group)一起写入 binlog 并刷盘。last_committed 和 sequence_number。last_committed 相同的事务,表示它们属于同一个组,可以在从库并行执行。工作原理:
主库:事务在 prepare 阶段会进入一个队列。当某个事务要刷盘时,它会将其队列中所有已经 prepare 好的事务一起打包成一个组。
binlog:记录形式如下。last_committed 相同的事务(如 6、7、8)可以并行。
# sequence_number=6 last_committed=5 ... # sequence_number=7 last_committed=5 ... # sequence_number=8 last_committed=5 ... # sequence_number=9 last_committed=8 ... -- 必须等8提交后才能执行
从库:协调线程(coordinator thread)会读取这些信息,将 last_committed 相同的事务分发给不同的工作线程(worker thread)并行执行。只有 last_committed 小于当前已执行事务 sequence_number 的事务才能被分发,以保证依赖关系。
优点:并行粒度从数据库级别细化到了事务级别,只要事务在主库上是并行提交的,在从库就能并行回放,效果非常好。
配置:
slave_parallel_workers = 8 slave_parallel_type = logical_clock -- 控制组提交的积极性,值越小组提交越频繁,意味着从库并行度可能更高 binlog_group_commit_sync_delay = 100 -- 微秒级延迟,以等待更多事务成组 binlog_group_commit_sync_no_delay_count = 10 -- 达到一定数量立即成组
在 5.7 的基础上,mysql 8.0 引入了更先进的 writeset 策略,进一步提升了并行效率。
策略:不再依赖主库的组提交时机,而是直接分析事务本身修改了哪些数据。
writeset,这是一个集合,包含了本事务修改的所有行的唯一哈希值(由库名、表名、主键或唯一索引键值计算得出)。writeset 没有交集,即它们修改的不是同一行,就说明它们没有冲突,可以并行。writeset 历史映射(在内存中),记录最近修改过某行数据的事务的 sequence_number。当一个新事务到来时,系统会检查其 writeset 中的每一行,找出所有修改过这些行的最新事务的 sequence_number,其中最大的那个数,就是这个新事务的 last_committed 值。优点:
last_committed。binlog_group_commit_sync_delay),即使主库没有故意延迟提交,从库也能获得很好的并行效果。配置:
slave_parallel_workers = 16 slave_parallel_type = logical_clock -- writeset 是 logical_clock 的增强子功能 -- 在主库上开启 writeset 识别(也用于增强半同步复制) transaction_write_set_extraction = xxhash64 -- 计算 writeset 的哈希算法 binlog_transaction_dependency_tracking = writeset -- 依赖跟踪模式:commit_order(5.7默认), writeset, writeset_session
在启用并行复制后,从库的 sql 线程演变为一个协调者线程(coordinator) 和多个工作线程(worker threads) 的架构:
i/o thread:保持不变,负责从主库拉取 binlog 事件并写入本地的中继日志(relay log)。
coordinator thread:
worker threads:多个工作线程,真正负责执行被分配到的 relay log 中的事务。它们是并行的执行单元。
想象一下,主库是一个接一个地发出指令的指挥官(串行提交事务),从库是一队士兵。
指挥官发出指令时没有特意 grouping(因为压力小,指令是串行发出的)。那么从库的士兵们就会认为:“指令1、2、3是在不同时间收到的,它们必须按顺序执行。”
所以执行顺序是:a完成 -> b开始并完成 -> c开始并完成。
结果: 士兵b明明可以去独立完成任务,却被迫要等士兵a完成后才能开始。效率低下。
从库这边有一个超级智能的参谋长。他拿到指令清单后,会分析每条指令的具体内容:
参谋长的推理:
于是,参谋长制定了这样一个并行执行计划:
最终结果:
在writeset模式下,主库在写binlog时,会基于writeset历史映射表,为每个事务重新计算一个last_committed值。
last_committed值会被设为上一个全局事务的序号(比如0)。last_committed值会被设置为和指令1相同(也就是0)。last_committed值会被设置为指令1的sequence_number(比如1)。从库的协调线程看到的是:
事务1: last_committed=0, sequence_number=1 事务2: last_committed=0, sequence_number=2 // 与事务1的last_committed相同! 事务3: last_committed=1, sequence_number=3 // 必须等seq_num=1的事务完成
协调规则没变:last_committed相同的事务可以并行。
sequence_number <= 1的事务(即事务1)完成后才能开始。(事务2是否完成不影响事务3的开始)到此这篇关于详解mysql并行复制的原理的文章就介绍到这了,更多相关mysql并行复制内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论