找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

914

积分

0

好友

128

主题
发表于 11 小时前 | 查看: 1| 回复: 0

MySQL的运维实践中,大事务一直是个令人头疼的痛点。它不仅会导致显著的复制延迟,更会引发各种稳定性问题。上一篇文章讨论了《MySQL大事务提交优化》,解决了大事务在提交阶段写Binlog导致的性能瓶颈。那么,除了提交阶段的写盘瓶颈,半同步复制时的Binlog传输是否会成为新的阻塞点?本文就将深入剖析大事务在半同步复制场景下的问题根源,并详细介绍AliSQL中创新的解决方案。

在《MySQL大事务提交优化》中,我们曾指出大事务提交写Binlog会导致系统出现一些诡异的慢SQL现象。例如:
MySQL慢查询日志截图显示大事务导致的延迟

  • 平时执行很快的INSERT语句,竟然耗时1.3s,且慢日志中并未显示有长时间的锁等待。
  • 多语句事务的所有DML语句都已执行完毕,但最后的COMMIT语句却执行了1.3s

实际上,这种“小语句莫名变慢”的现象,不仅由大事务写Binlog引起,在半同步复制环境下,大事务的Binlog网络传输同样会导致类似表现。我们通过一个模拟测试来验证:使用sysbench的oltp_write_only模拟正常写业务,同时在后台提交一个产生了2GB Binlog Events的大事务(该事务已应用提交优化)。从监控图可以看到,在大事务提交期间,业务QPS瞬间跌至0点,直到半同步复制超时才得以恢复。
半同步复制下大事务导致QPS跌零的性能监控图

根因分析:传输队列的阻塞效应

要理解问题的本质,我们需要回顾半同步复制的事务提交流程。下图清晰地展示了这一过程:
MySQL半同步复制事务提交流程图

  1. 事务提交时,首先进行两阶段提交的Prepare阶段。
  2. 将事务的Binlog Events写入Binlog文件。
  3. 写入完成后(after_sync模式),开始等待自己的Binlog Events被发送到至少一个备库并收到确认(ACK)。
  4. Binlog Dump线程负责按顺序将Binlog文件中的Events发送给备库。
  5. 备库的IO线程接收这些Events,并将其写入本地的Relay Log文件。
  6. 备库接收到完整事务后,会向主库发送一个包含Binlog文件名和位点信息的ACK应答。
  7. 主库的ACK Receiver线程根据位点信息唤醒对应的事务。
  8. 事务被唤醒后,继续完成提交操作,并向客户端返回成功。

问题的关键在于:主备之间只有一个Binlog Dump线程,它严格按照Binlog Events的写入顺序进行串行传输。这意味着,如果队列前方出现一个包含海量Events的大事务,那么后续所有的小事务都必须等待这个大事务的所有Events发送完毕,才能轮到它们。这种等待时间包含了前方大事务的全部传输耗时,因此就会出现小事务提交异常缓慢的情况。

MySQL提供了rpl_semi_sync_master_timeout参数来应对这种长时间阻塞。当事务等待ACK的时间超过该阈值后,复制会自动退化为异步模式,从而恢复主库的写入能力。但这是一种“断臂求生”的策略,牺牲了数据复制的同步性。

RPO=0方案面临的挑战

正因为半同步复制要求事务在备库落盘后才能提交,它常被用作构建RPO(Recovery Point Objective)=0高可用方案的基础。一个典型的架构是“一主两备”:
基于半同步复制的一主两备高可用架构图
在此架构中,事务需收到任意一个备节点的ACK才能提交,从而保证即使主节点宕机,数据也至少存在于一个备节点上。

然而,要真正实现RPO=0,就必须保证半同步永不退化。MySQL半同步存在两个可能退化的点:一是宕机重启后已写入Binlog但未同步的事务会被提交;二是等待超时。其中,大事务是导致第二个退化点的最常见原因。若为了保证一致性而将超时时间设置得极大,一个大事务就足以导致整个集群长时间不可写。因此,许多方案不得不妥协,选择在大事务出现时临时降级为异步,但这又与RPO=0的目标相悖。

AliSQL的解决方案:大事务实时传输机制

为了从根本上解决这一问题,AliSQL设计并实现了一套大事务实时传输机制。该机制的核心思想是:将大事务的Binlog Events传输从“提交阶段”提前到“执行阶段”,从而避免在提交点形成传输瓶颈。
AliSQL大事务实时传输机制架构图

这套机制的关键运作步骤如下:

  1. 大事务识别与注册:在DML语句执行期间,当事务产生的Binlog Events体积超过特定阈值时,该事务会被标记并注册到一个“大事务列表”中。
  2. 实时流式传输:Binlog Dump线程会主动读取大事务列表,从这些事务的Binlog临时文件中读取Events,并流式传输到备库。传输过程做了智能限流,优先保证正常事务的Binlog文件Events发送,避免影响当前提交的事务。
  3. 备库缓存(Relay Log Cache):备库IO线程收到带有特殊标记的大事务Events后,将其暂存到一个临时文件中,称为Relay Log Cache
  4. 提交与确认:当大事务在提交时,Dump线程只需发送一个很小的Gtid_event到备库。备库收到此事件后,便知该事务的所有Events已接收完毕。
  5. 缓存转换:备库将Relay Log Cache转换为一个完整的Relay Log文件,供SQL线程消费。
  6. 并发支持:该机制支持多个大事务同时进行实时传输。

由此可见,大事务的绝大部分数据在DML执行过程中就已“化整为零”地传输到了备库。提交阶段仅需传输一个轻量的Gtid_event,因此不会阻塞其他任何事务的传输,也消除了突发性的大流量对网络的冲击。

关键技术:Relay Log Cache

实时传输机制与之前的大事务提交优化一脉相承,并复用了部分核心逻辑。当事务的Binlog Events超过binlog_cache_size后,会写入临时文件。实时传输机制利用了这个临时文件,并在备库侧设计了对应的Relay Log Cache
Relay Log Cache转换为Relay Log文件的过程示意图
Relay Log Cache的头部会预留空间。当将其转换为正式的Relay Log文件时,会填充必要的格式描述事件(Format_description_event)等头部信息,使其成为一个合法的、可供SQL线程直接读取的Relay Log。

异常处理保障健壮性

大事务执行周期长,必须妥善处理各类异常:

  • 主库回滚:如果大事务在主库回滚,Dump线程会向备库发送rollback指令,备库IO线程收到后销毁对应的Relay Log Cache
  • 连接中断:如果主备连接断开或执行了STOP SLAVE,备库IO线程会清理所有Relay Log Cache。待重新连接后,所有大事务的实时传输会从头开始。

优化效果验证

我们使用同样的测试场景进行验证:sysbench模拟正常写负载,后台提交2GB Binlog的大事务。启用实时传输机制后的效果对比如下:
启用实时传输后QPS运行平稳的监控图
可以看到,业务写入曲线(QPS)在整个测试期间运行平稳,完全避免了因大事务传输导致的业务“跌零”现象。

总结

在传统的MySQL半同步复制架构中,大事务的Binlog传输阻塞是一个经典难题。为了保障系统稳定性,运维人员往往需要在“严格约束业务消除大事务”和“容忍复制降级为异步”之间艰难抉择。AliSQL的大事务实时传输机制创新性地将传输时机前置,变“提交时批量传输”为“执行时流式传输”,从根本上破解了阻塞问题。这不仅提升了半同步复制集群的稳定性和性能,更让基于半同步构建真正意义上的RPO=0高可用方案成为可能,为数据库的高可用实践提供了坚实的技术支撑。更多深入的数据库技术讨论,欢迎在云栈社区交流。




上一篇:深入解析Go语言sync.RWMutex:高并发读多写少场景的锁优化方案
下一篇:Gemini API国内可用方案:二次开发NanoBanana PPT生成工具踩坑指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-27 16:58 , Processed in 0.256259 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表