本页内容受版权保护 · 已添加水印 · 禁止任何形式转载
主从复制与高可用 · 复制原理

并行复制的几种策略:库级并行、组提交、WRITESET?performance_schema.replication_applier_status_by_worker?

Parallel replication strategies: database-level, group commit, WRITESET? performance_schema

答案语言

考察要点

这道题面试官想看你是否深刻理解 MySQL 主从复制中的性能瓶颈以及对应的解决方案演进。核心考察点包括:

  1. 复制瓶颈的认知:是否明白传统单线程复制的瓶颈在于备库的 SQL Thread。
  2. 并行复制策略的演进:是否能清晰阐述从库级并行(5.6)、基于组提交的逻辑时钟并行(5.7)到基于 WRITESET 的真正并行(8.0)这三代技术的原理、优缺点和适用场景。
  3. 实践与监控能力:是否知道如何配置、监控(特别是 performance_schema 的使用)和排查并行复制中的问题。

核心概念

MySQL 主从复制的核心瓶颈之一在于备库(Replica/Slave)应用中继日志(Relay Log)的环节。传统上,这个过程由一个单独的 SQL 线程串行执行,当主库(Source/Master)并发量很高时,备库很容易因为单线程执行能力不足而产生延迟(Lag)。并行复制就是为了解决这个问题而引入的。

1. 策略一:库级并行(Database-level Parallelism)

这是 MySQL 5.6 引入的最早的并行复制策略。

  • 原理:基于 "库"(Schema)进行并发。它为每个 Schema 分配一个 Worker 线程。如果一个事务跨多个库,它仍然会在前一个事务执行完毕后才执行,以保证一致性。
  • 配置:
    sql
    1-- MySQL 5.6
    2SET GLOBAL slave_parallel_workers = N; -- N > 0, 开启并行复制
    3-- slave_parallel_type 参数在 5.7 中引入,5.6 默认为库级
  • 优点:
    • 实现简单,相比单线程有一定提升。
  • 缺点:
    • 效果有限:并行度受限于 Schema 数量。如果业务压力集中在单一或少数几个库上,并行复制会退化成单线程执行,无法解决核心瓶颈。
    • 跨库事务限制:跨库事务会成为同步点,降低并行效率。

2. 策略二:基于逻辑时钟的并行(LOGICAL_CLOCK)

这是 MySQL 5.7 引入的,也是 5.7 的默认并行策略,实现了真正的实例级并行。

  • 原理:利用了主库的组提交(Group Commit)机制。在主库上,能够同时提交的一组事务,在备库上也可以并行应用。因为它们在主库上处于同一个 "commit group",逻辑上没有依赖关系。主库在生成 Binlog 时,会为同一组提交的事务打上相同的 last_committed 值(可以理解为一个逻辑时间戳),备库的协调线程(Coordinator Thread)根据这个时间戳将事务分发给不同的 Worker 线程。
  • 配置:
    sql
    1-- MySQL 5.7+
    2SET GLOBAL slave_parallel_workers = N;
    3SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
  • 优点:
    • 真正的实例级并行:打破了库的界限,只要主库并发度高,备库就能获得很好的并行加速效果。
  • 缺点:
    • 依赖主库并发度:如果主库本身并发低,或者事务都是串行提交的,那么备库的并行效果会大打折扣。
    • 部分场景失效:在某些情况下(如外键约束、XA 事务),为了保证数据一致性,并行度会受到限制。

3. 策略三:基于 WRITESET 的并行

这是 MySQL 8.0 引入的,是目前最先进的策略。它通过分析事务实际修改的数据行来决定是否可以并行。

  • 原理:在主库上,当一个事务提交时,会计算该事务修改的所有行的主键(或唯一键)的哈希值,这个集合称为 Writeset。这个 Writeset 会被记录到 Binlog 中。备库的协调线程在分发事务时,会检查不同事务的 Writeset 是否有交集。只要两个事务的 Writeset 没有交集,就意味着它们没有数据冲突,无论它们在主库上的提交顺序如何,都可以在备库上并行应用。
  • 配置:
    sql
    1-- MySQL 8.0+
    2SET GLOBAL slave_parallel_workers = N;
    3SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK'; -- 8.0 仍然使用此类型名,但底层机制由 WRITESET 驱动
    4SET GLOBAL binlog_transaction_dependency_tracking = 'WRITESET'; -- 在主库设置,决定 binlog 中是否记录依赖信息
  • 优点:
    • 最大化并行度:不依赖主库的提交顺序,只关心数据本身是否冲突,挖掘了最大的并行潜力。即使主库是串行提交的两个不冲突的事务,备库也能并行执行。
  • 缺点:
    • 要求表有主键:对于没有主键的表,更新或删除操作会退化为表级锁,其 Writeset 会是整张表,导致与该表相关的其他事务都无法并行。因此,**所有表都必须有主键(或非空唯一键)**是发挥 WRITESET 性能的前提。
    • 计算开销:在主库上计算和记录 Writeset 会带来微小的性能开销。

策略对比总结

特性库级并行 (DATABASE)逻辑时钟 (LOGICAL_CLOCK)写集 (WRITESET)
MySQL 版本5.65.78.0
并行粒度Schema 级事务组级行级(基于主键)
并行依据事务操作的库不同主库上同时提交事务修改的行不冲突
核心优点实现简单打破库限制并行度最大化
核心缺点依赖多库负载均衡依赖主库并发度强依赖表主键

4. 监控视图:performance_schema.replication_applier_status_by_worker

这个视图是诊断并行复制性能的关键。它展示了每个 Worker 线程的当前状态。

  • 关键字段:

    • WORKER_ID: Worker 线程的 ID。
    • THREAD_ID: 对应的系统线程 ID。
    • SERVICE_STATE: ON 或 OFF。
    • LAST_SEEN_TRANSACTION: 该 Worker 最后处理的事务 GTID。
    • LAST_ERROR_NUMBER: 最后发生的错误码。
  • 使用示例:

    sql
    1SELECT
    2 WORKER_ID,
    3 THREAD_ID,
    4 SERVICE_STATE,
    5 LAST_SEEN_TRANSACTION
    6FROM
    7 performance_schema.replication_applier_status_by_worker
    8ORDER BY
    9 WORKER_ID;

    通过观察这个视图,可以判断:

    • 并行度是否生效:是否有多个 Worker 处于活动状态。
    • 是否存在瓶颈:如果大部分 Worker 都在等待,而少数 Worker 一直在忙,可能遇到了大事务或 DDL,阻塞了其他事务。
    • 是否出错:检查 LAST_ERROR_NUMBER 和 LAST_ERROR_MESSAGE 字段。

常见低分回答

  1. 混淆概念:将 LOGICAL_CLOCK 和 WRITESET 的原理搞混,比如错误地认为 5.7 的并行是基于数据行冲突检测的。
  2. 不知前提条件:只说 WRITESET 最好,但不知道它强依赖于所有表都必须有主键。在面试官追问“如果我的业务表就是没主键怎么办”时无法回答(正确答案是 WRITESET 效果会急剧下降,甚至不如 LOGICAL_CLOCK)。
  3. 回答过于笼统:只说“并行复制就是多线程复制,可以加快速度”,而无法清晰阐述不同策略的原理、演进关系和优劣。
  4. 纸上谈兵,缺乏实践:知道理论,但当被问及“如果开启并行复制后延迟依旧,如何排查?”时,说不出要去看 performance_schema 或分析大事务、DDL 等具体步骤。
  5. 忽略版本差异:不清楚哪个版本支持哪种策略,或者错误地将 8.0 的特性安在 5.7 上。

工程实践与优化

  • 策略选择:

    • MySQL 8.0+:首选并默认使用 WRITESET。务必保证所有表都有主键。
    • MySQL 5.7:使用 LOGICAL_CLOCK。可以通过调整主库的 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 来适当增大组提交的事务数量,从而提升备库的并行度。
    • MySQL 5.6:使用 DATABASE 策略,但更推荐的方案是尽快升级到更高版本。
  • 参数调优:

    • slave_parallel_workers:通常设置为备库 CPU 核心数的 1-2 倍,但需要根据实际负载(CPU、IO)进行压测调整。并非越大越好,过多的线程会增加调度开销。
    • slave_preserve_commit_order=1 (8.0+):为了在备库上获得与主库完全一致的提交顺序(例如,为了逻辑备份的一致性),可以开启此参数。它会确保事务并行应用后,按主库顺序提交,但这可能会引入提交等待,略微影响性能。
  • 瓶颈排查:

    1. 确认延迟:通过 SHOW SLAVE STATUS \G 查看 Seconds_Behind_Master。
    2. 检查 Worker 状态:查询 performance_schema.replication_applier_status_by_worker,看是所有 Worker 都慢,还是某个 Worker 被阻塞。
    3. 定位阻塞事务:如果发现某个 Worker 长时间处理一个事务,可以通过 performance_schema.events_statements_current 结合 THREAD_ID 找到该 Worker 正在执行的 SQL 语句,判断是否为大事务或 DDL。
    4. DDL 影响:DDL 操作通常会阻塞并行复制,因为它需要等待所有前面的事务都执行完毕才能执行,并且会阻塞后面的事务。这是并行复制的一个“同步点”。

常见追问与延伸题目

  1. 追问:WRITESET 听起来很完美,它有什么性能开销或潜在的坑吗?

    • 回答要点 1 (性能开销):WRITESET 的确有开销。主库在事务提交时需要额外做两件事:1) 计算事务修改行的哈希值;2) 将这些哈希值(即 Writeset)写入 Binlog。这会增加主库的 CPU 消耗和 Binlog 的大小。不过,在大多数场景下,这点开销远小于并行复制带来的收益。
    • 回答要点 2 (无主键的坑):最大的坑是无主键的表。对无主键表的 UPDATE 或 DELETE,MySQL 无法确定具体修改了哪些行,只能认为整个表都被修改了。其 Writeset 会退化为“全表”,导致任何其他对该表的操作都无法与之并行,严重破坏并行效果。
    • 回答要点 3 (哈希冲突):Writeset 使用 XXHASH64 算法,理论上存在哈希冲突的可能(尽管概率极低)。如果发生冲突,两个本不冲突的事务会被误判为冲突,导致它们被串行执行,只是损失了一点并行度,但保证了数据不错乱,所以是安全的。
  2. 追问:如果开启了并行复制,但 Seconds_Behind_Master 依然很高,你会从哪些方面排查?

    • 回答要点 1 (区分瓶颈环节):首先要确定瓶颈是在 IO Thread 还是 SQL Thread。查看 SHOW SLAVE STATUS \G,比较 Relay_Log_Pos 和 Exec_Master_Log_Pos。如果两者差距很大,说明 IO Thread 拉取 Binlog 慢,可能是网络问题或主库 IO 压力大。如果两者接近,但 Exec_Master_Log_Pos 远落后于主库的 File 和 Position,说明瓶颈在 SQL Thread 应用 Relay Log。
    • 回答要点 2 (分析 SQL Thread 瓶颈):既然并行复制已开,瓶颈大概率是:
      • 大事务/慢SQL:一个超大事务(如一次性 DELETE 或 UPDATE 几百万行)会长时间占据一个 Worker,并可能阻塞依赖它的其他事务。
      • DDL 操作:如上所述,DDL 是一个同步点,会暂停并行应用。
      • 资源瓶颈:备库的 CPU、IOPS 或内存不足,即使有多线程,整体处理能力也上不去。
      • 配置不当:slave_parallel_workers 数量设置不合理。
    • 回答要点 3 (排查步骤):我会按以下步骤操作:1) SHOW SLAVE STATUS 定位瓶颈线程。2) 查询 performance_schema.replication_applier_status_by_worker 看 Worker 状态。3) 结合 SHOW PROCESSLIST 和 performance_schema 找到卡住的 SQL。4) 检查备库的系统资源监控(CPU、iostat、free)。5) 检查 MySQL 错误日志。
  3. 追问:MySQL 的 MGR(Group Replication)也提到了 Writeset,它和主从复制的 Writeset 是一回事吗?

    • 回答要点 1 (概念共通):是的,底层的核心概念是共通的。两者都使用 Writeset 来检测事务之间的数据冲突。Writeset 就是事务修改行的主键哈希集合。
    • 回答要点 2 (应用阶段不同):这是最关键的区别。
      • 主从复制 (Async Replication):Writeset 是在主库事务提交后生成并写入 Binlog,用于备库并行应用。它是一种乐观的并行化,假设事务可以并行,在备库应用时解决依赖。
      • 组复制 (Group Replication):Writeset 是在事务提交前,在组内各个节点间广播,用于冲突认证 (Certification)。如果一个事务的 Writeset 与其他已认证但未提交的事务冲突,该事务会被回滚。它是一种悲观的并发控制,在提交前就解决冲突,以保证组内数据的一致性。
    • 回答要点 3 (目的不同):主从复制的 Writeset 是为了提升备库追数据的性能。MGR 的 Writeset 是为了实现分布式事务的冲突检测和共识,是保证多写节点数据一致性的核心机制。