大数据技术

MySQL:半同步复制写入延迟从异步的0.5ms涨到2-5ms,并发一叠加就顶不住,什么业务值得为“少丢数据”付这个代价?

👤 为我痴狂 👁 23 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-02
首页› 大数据› 大数据技术› 正文
MySQL 高可用 · 复制一致性 · 性能工程
MySQL:半同步复制写入延迟从异步的0.5ms涨到2-5ms,并发一叠加就顶不住,什么业务值得为“少丢数据”付这个代价?

从 0.5ms 到 2-5ms,不是一次简单的“变慢”,而是一次一致性成本的显性化。本文把这条延迟曲线拆成可测量的分量,回答一个工程问题:哪些业务应该为它付费,哪些不该。

摘要

MySQL 异步复制下,一次主库提交只需写本地 redo/binlog 即可返回,实测单条点写延迟常在 0.3–0.8ms;开启半同步复制(semi-sync)后,主库必须等待至少一个从库回传 ACK 才能向客户端返回,延迟抬升到 2–5ms,并且在并发上升时出现非线性劣化。本文以“一致性成本”为唯一分析主线,把这段增量拆解为网络往返、从库落盘与 ACK 回传、主库等待线程唤醒、组提交(binlog group commit)节拍对齐四个可测量分量,给出可复现的压测方法与参数调优路径,并据此划定“值得付费”的业务判据。

核心结论:半同步的代价不是固定税,而是随并发与网络抖动放大的乘性成本;是否采用应由“单次数据丢失的期望损失 × 丢失概率”与“延迟上升导致的吞吐损失”做量化对冲,而非默认“更安全就更好”。

一、问题定义:0.5ms 与 2-5ms 之间到底差了什么

1.1 先把两个数字的测量口径对齐

讨论“异步 0.5ms、半同步 2-5ms”之前,必须先固定测量口径,否则数字之间不可比。工程上至少有四种延迟口径:客户端视角的端到端 P99、代理层(ProxySQL/MySQL Router)观测的查询耗时、主库 performance_schema 中的语句延迟、以及 binlog 落盘到从库 ACK 的复制链路时延。本文讨论的“写入延迟”默认指客户端发起 INSERT/UPDATE 到收到 OK 包的端到端时延,在单连接、无锁冲突、小事务(单行、自动提交)条件下测得。

在这个口径下,异步复制的 0.5ms 量级是合理的:一次自动提交事务需要写 redo log(受 innodb_flush_log_at_trx_commit=1 约束需 fsync)、写 binlog(受 sync_binlog=1 约束需 fsync)、提交事务并返回。在 NVMe SSD 上,两次 fsync 的合并与组提交优化后,单条写入落在亚毫秒区间是常见结果。而半同步把这中间插入了一个跨主机的往返确认,物理下限立刻被网络 RTT 锁死。

关键认知:半同步增加的延迟不是软件开销,而是物理距离的货币化。同机房 RTT 约 0.1–0.3ms,同城双活约 0.5–1.5ms,跨城约 20–40ms。2-5ms 的观测值通常对应“同机房 + 从库落盘 + 线程调度 + 组提交节拍”的叠加,而非单纯网络。

1.2 为什么“并发一叠加就顶不住”才是真问题

单条延迟从 0.5ms 涨到 3ms,按 Little 定律粗算,单线程吞吐从 2000 TPS 降到约 333 TPS,这还只是线性损失。真正让系统“顶不住”的是非线性部分:半同步的等待发生在主库的会话线程上,等待期间该线程占用连接、占用可能的行锁与 MDL,等待队列变长后,锁持有时间被拉长,冲突概率上升,进而触发更多等待——这是一个正反馈。本文评述:很多团队把半同步当作一个“开关”,却忽略它改变的是系统的排队结构,而不只是单次操作的耗时。

更隐蔽的是超时退化行为。MySQL 半同步在等待超时(rpl_semi_sync_master_timeout,默认 10000ms)后会退化为异步,并在下一个事务尝试恢复半同步。这意味着在从库抖动时,系统会在“慢”和“可能丢数据”两种状态间反复横跳,延迟曲线呈现锯齿状。生产上观测到的“偶发卡顿几秒”往往源于此,而非慢查询。

二、机制拆解:半同步的四段延迟与并发放大效应

2.1 协议流程:AFTER_SYNC 与 AFTER_COMMIT 的语义差异

MySQL 5.7 起引入 rpl_semi_sync_master_wait_point 参数,提供两种等待点。AFTER_COMMIT(5.7 之前的行为):主库先提交事务、释放锁,再等待从库 ACK,然后才向客户端返回。此时其他会话可能已经读到该事务的修改,若主库崩溃且从库未收到,则出现“幻读丢失”。AFTER_SYNC(5.7 默认):主库在 flush binlog 之后、提交存储引擎之前等待 ACK,收到后再提交并向客户端返回。本文评述:AFTER_SYNC 把等待点前移,牺牲的是主库锁持有时间,换来的是“客户端可见即已复制”的强语义,这是当前工程上应默认选择的模式。

需要强调一个常被误读的边界:半同步保证的是“至少一个从库收到并写入 relay log(或应用)”,而非“从库已提交”。在 AFTER_SYNC 模式下,从库 ACK 的时机取决于 rpl_semi_sync_slave_enabled 与从库的 relay log 落盘策略。若从库 sync_relay_log=0,ACK 可能在内存写入后即发出,此时主库宕机 + 从库宕机仍可能丢数据。因此“半同步 = 不丢数据”是一个需要加限定条件的命题。

2.2 四段延迟分量:把 2-5ms 拆开看

把一次半同步写入的额外延迟记为 Δ,可分解为:

Δ = T_net_rtt + T_slave_io + T_slave_flush + T_ack_return + T_master_wakeup + T_group_align

其中:
  T_net_rtt      主→从单向网络传输 + 从→主 ACK 回传(物理下限)
  T_slave_io     从库 IO 线程写 relay log 的耗时(含可能的 fsync)
  T_slave_flush  从库 SQL 线程应用(取决于并行复制与 sync_relay_log 策略)
  T_ack_return   ACK 包在从库侧的排队与发送延迟
  T_master_wakeup 主库等待线程被唤醒、重新调度上 CPU 的时间
  T_group_align  组提交节拍对齐带来的额外等待(见 2.3)

在同机房千兆/万兆网络下,T_net_rtt 约 0.1–0.3ms;T_slave_io 在 NVMe 上约 0.1–0.5ms(若 relay log 不强制 fsync 则更短);T_master_wakeup 受 CPU 调度影响,空闲时几十微秒,负载高时可达毫秒级;T_group_align 则与并发度强相关。四者叠加即可解释 2-5ms 的观测区间。本文评述:把 Δ 拆成可测分量,是后续一切优化的前提——否则调参就是盲调。

2.3 组提交与半同步的节拍冲突

MySQL 的 binlog 组提交把多个并发事务的 fsync 合并为一次,这是异步复制能跑到数万 TPS 的关键。但半同步引入了一个“等待外部事件”的阶段,与组提交的批处理节拍存在结构性张力。在 AFTER_SYNC 模式下,主库在 flush 阶段后等待 ACK,此时同一组的其他事务也被阻塞在等待队列中;当 ACK 返回,整组一起提交。这带来两个后果:其一,组越大,单次等待被摊薄得越好,但组越大,尾部事务的等待也越长;其二,ACK 的到达时间若与组提交节拍错位,会产生“空等”——组已就绪但 ACK 未到,或 ACK 已到但组未凑齐。

这解释了一个反直觉现象:半同步在低并发下延迟接近 RTT,在中等并发下延迟反而可能先降后升——因为组提交摊薄了 fsync,但等待队列的排队延迟开始主导。工程上应通过 binlog_group_commit_sync_delay 与 binlog_group_commit_sync_no_delay_count 调节节拍,但这两个参数对半同步的影响需要实测,不能照搬异步场景的经验值。

延迟分量 典型量级(同机房) 主要影响因素 可优化手段
T_net_rtt 0.1–0.3ms 物理距离、交换机、MTU 同 AZ 部署、RDMA/DPDK
T_slave_io 0.1–0.5ms relay log 落盘策略、IO 负载 NVMe、合理设置 sync_relay_log
T_master_wakeup 0.05–1ms CPU 争用、线程数、NUMA 绑核、控制并发、减少上下文切换
T_group_align 0–2ms(随并发上升) 组提交参数、并发度 调 sync_delay、控制写入并发

表 1:半同步额外延迟的四段分解(量级为同机房 NVMe 环境下的工程经验区间,非单一文献数据,供建模参考)。

三、量化模型:用可测分量预测你的延迟与吞吐拐点

3.1 从单条延迟到系统吞吐的映射

设主库可用并发工作线程数为 N(受 innodb_thread_concurrency、连接池大小、CPU 核数共同约束),单事务在无等待时的服务时间为 S,半同步额外等待为 Δ,则稳态吞吐的近似上界为:

TPS_max ≈ N / (S + Δ)

若 Δ 与并发相关(Δ = Δ0 + k·N),则:
TPS_max ≈ N / (S + Δ0 + k·N)  →  N→∞ 时趋近 1/k

即:半同步系统的吞吐存在一个由 k 决定的天花板,
单纯加并发无法突破,反而会推高 P99。

这个模型的价值在于给出可证伪的预测:如果实测 TPS 随并发增加而饱和,且饱和点与 1/k 吻合,则说明瓶颈在等待队列而非 CPU;若 TPS 持续下降,则说明锁冲突或线程调度已介入。本文评述:工程上不需要精确的 k,只需要判断“当前是否已过拐点”,这可以通过压测曲线的二阶导数快速识别。

3.2 一个可复现的压测方法

建议用 sysbench 的 oltp_write_only 场景,固定数据集(如 10 张表 × 10 万行),分别在一主一从的异步与半同步配置下,以 1/4/8/16/32/64 线程阶梯压测,每档运行 300 秒,记录 TPS、P50/P95/P99 与主库 Threads_running、rpl_semi_sync_master_net_wait_time 等指标。数据预处理上,剔除前 30 秒预热与后 30 秒收尾,取中间 240 秒的稳定区间;对 P99 采用分位数聚合而非平均值,避免长尾被稀释。

关键观测点有三个:其一,半同步相对异步的 TPS 衰减比是否随线程数扩大;其二,P99 与 P50 的比值是否在某个线程数后急剧上升(拐点信号);其三,rpl_semi_sync_master_wait_sessions 是否持续大于 0。若第三项长期为正,说明等待队列已成瓶颈,加并发无益。

四、工程路径:压测、调参与架构降本的完整操作步骤

4.1 第一步:确认你真的需要半同步

在调参之前,先回答一个前置问题:你的 RPO(恢复点目标)到底是 0 还是“秒级”?异步复制在单机故障下的丢失窗口等于主从延迟,通常几十毫秒到数秒;若业务能接受“最近 N 秒数据可丢”,则半同步的收益为零,代价却是实打实的。本文评述:RPO 的量化是决策的起点,很多团队在未定义 RPO 的情况下开启半同步,属于用工程复杂度对冲一个未被量化的风险。

若 RPO 确为 0,还需区分“单机故障不丢”与“机房级故障不丢”。半同步只保证至少一个从库收到,若该从库与主库同机房,机房级故障仍会丢;此时需要的是跨 AZ 的半同步或基于共识协议的方案(如 MySQL Group Replication、Paxos 系产品)。边界条件必须写清楚,否则“开了半同步就安全”是一种危险的错觉。

4.2 第二步:参数调优的优先级顺序

调参应按“影响面 × 可逆性”排序,而非按网上流传的清单。优先级建议如下:

  1. 等待点:确认 rpl_semi_sync_master_wait_point=AFTER_SYNC,这是语义正确性的基础,不可妥协。
  2. 超时值:rpl_semi_sync_master_timeout 默认 10s 过长,会导致故障时长时间卡顿;建议按业务 P99 的 3–5 倍设置(如 100–500ms),并配合监控告警,让退化行为快速可见。
  3. ACK 接收线程:rpl_semi_sync_master_enabled 与从库 rpl_semi_sync_slave_enabled 必须成对开启,且从库重启后需检查插件是否自动加载。
  4. 组提交节拍:谨慎调整 binlog_group_commit_sync_delay,半同步场景下过大的 delay 会叠加等待,建议从 0 开始,以 100μs 步进实测。
  5. 从库落盘:评估 sync_relay_log 与 innodb_flush_log_at_trx_commit 在从库上的取值,权衡 ACK 时延与从库崩溃后的可恢复性。

需要提醒的是,rpl_semi_sync_master_wait_for_slave_count(MySQL 8.0.27+ 支持)允许要求多个从库确认,这会线性放大延迟,仅在金融级场景且从库同 AZ 时考虑。本文评述:参数不是越多越好,每增加一个确认副本,就多一份网络与落盘的乘性成本。

4.3 第三步:架构层的降本手段

当参数调优触顶,应从架构层降低半同步的暴露面。常见手段有四类:其一,读写分离 + 写路径收敛,把非关键写(日志、埋点、计数)路由到异步实例,仅核心交易走半同步;其二,批量合并,把高频小事务合并为大事务,减少 ACK 次数,但需注意大事务对复制延迟与锁的影响;其三,就近从库,确保半同步从库与主库同 AZ,把 T_net_rtt 压到最低;其四,异步补偿,对可对账的业务采用异步复制 + 定时对账 + 幂等重放,用业务逻辑兜底而非数据库层强一致。

第四类手段值得展开。对账补偿的本质是把“数据库层的一致性成本”转移到“业务层的最终一致性成本”,后者通常更便宜,因为对账可以离线、可以批处理、可以容忍分钟级延迟。本文评述:不是所有“不能丢”都需要“实时不丢”,把 RPO 从 0 放宽到 1 分钟,往往能换来数倍的吞吐,而业务感知为零。这一判断需要与业务方共同完成,而非 DBA 单方面决定。

方案 RPO 写入延迟增量 适用场景
异步复制 秒级(等于复制延迟) ≈0 日志、埋点、可对账业务
半同步(AFTER_SYNC,同 AZ) 单机故障≈0 2–5ms 核心交易、订单、支付
半同步(跨 AZ) 单 AZ 故障≈0 1–3ms(同城) 金融、账务
组复制 / 共识协议 多数派存活即不丢 更高(多轮协商) 强一致元数据、配置中心

表 2:不同复制方案的一致性—延迟权衡(延迟增量为工程经验区间,实际值需以自身环境压测为准)。

五、业务判据:什么业务值得为“少丢数据”付费

5.1 一个可操作的决策公式

把决策形式化:设单次数据丢失的期望损失为 L(含资金损失、合规罚款、用户信任折损的可货币化部分),异步复制下的年丢失概率为 p_a,半同步下为 p_s,半同步导致的吞吐损失折算为年成本 C_perf,则采用半同步的净收益为:

NetBenefit = L × (p_a − p_s) − C_perf

判据:NetBenefit > 0 时,半同步在经济上成立。

其中 C_perf 可由压测得到:
C_perf ≈ (TPS_async − TPS_semi) / TPS_async × 年度基础设施成本
        + 因延迟上升导致的转化率损失(如有)

这个公式的难点不在计算,而在给 L 赋值。本文评述:L 的赋值应由业务方完成并签字,DBA 提供 p_a、p_s、C_perf 的技术输入。实践中,L 的差异可达三个数量级——一条埋点日志的丢失损失接近零,一笔支付流水的丢失损失可能是数百元加上合规风险。用同一套复制策略覆盖所有表,本身就是一种资源错配。

5.2 值得付费的业务画像

综合 L 与写入特征,以下三类业务通常值得为半同步付费:其一,资金与账务类,单条记录丢失直接对应资金差错,且差错修复成本远高于延迟成本;其二,不可重放的业务事件,如订单创建、库存扣减、券核销,这些操作一旦丢失无法通过重试恢复(重试可能造成重复扣减);其三,合规敏感数据,如审计日志、医疗记录,丢失可能触发监管责任。这三类的共同特征是:丢失的期望损失 L 高,且写入 QPS 相对可控——注意后半句,高 L 但超高 QPS 的场景(如全站计数)需要的是分而治之,而非整体半同步。

反之,以下场景通常不值得:可重放的日志与埋点、可对账的统计类写入、缓存回填、以及写入 QPS 极高且单条价值极低的场景。本文评述:“少丢数据”的价值必须落到单条记录上计算,而不是笼统地说“数据很重要”。一个常见的反模式是:因为“数据都很重要”而全库开启半同步,结果核心交易的延迟被非核心写入拖累,整体体验下降,而真正的风险并未被精准覆盖。

5.3 混合策略:按表、按路径、按时间分级

工程上更现实的做法是分级而非二选一。按表分级:核心表走半同步实例,非核心表走异步实例,通过中间件路由;按路径分级:同步写路径走半同步,异步任务与补偿路径走异步;按时间分级:大促峰值期临时降级为异步并加强监控,平时保持半同步。第三种做法有争议,本文评述:降级必须是有预案、有监控、有回滚的显式操作,而非超时后的隐式退化。前者可控,后者会在故障时放大不确定性。

分级策略的落地依赖可观测性。至少需要监控:rpl_semi_sync_master_status(当前是否处于半同步)、rpl_semi_sync_master_no_tx(未确认事务数)、rpl_semi_sync_master_net_wait_time(网络等待累计时间)。当 no_tx 持续增长,说明退化已发生,需立即告警。

六、前沿与展望:从半同步到共识协议的演进逻辑

6.1 半同步的结构性局限

半同步的本质是“主库单点决策 + 从库被动确认”,它解决了“主库崩溃时至少有一个副本”的问题,但没有解决“确认副本本身也崩溃”以及“主库脑裂”的问题。在 MySQL Group Replication 中,基于 Paxos 变体的共识层要求多数派确认,才能在成员变更与网络分区下保持安全。本文评述:半同步是“一票确认”,共识协议是“多数派确认”,前者延迟低但容错边界窄,后者延迟高但语义更强。选择取决于故障模型,而非单纯的性能偏好。

近年学术界对复制协议的优化集中在“减少协商轮次”与“地理分布下的延迟优化”。例如,针对跨地域场景的共识协议通过让读请求本地化、写请求按地域分组来降低 RTT 影响;也有研究探索用硬件时钟(如 HLC)替代部分逻辑时钟以减少消息轮次。这些方向对 MySQL 生态的直接影响有限,但提供了判断趋势的框架:一致性成本正在从“协议层”向“部署拓扑层”转移,未来的优化空间更多在“把确认副本放得更近”而非“把协议改得更快”。

6.2 云原生与存算分离带来的变量

云数据库的存算分离架构改变了半同步的成本结构。当存储层由共享分布式存储(如基于 Paxos 的三副本)承担时,数据库实例的“提交”语义可能由存储层保证,此时应用层的半同步复制变得冗余——因为存储层已经提供了多副本确认。本文评述:在存算分离架构下,应重新评估“数据库层半同步”的必要性,避免在存储已多副本的前提下重复支付一致性成本。这一判断需要仔细核对云厂商的 SLA 与故障模型,不能想当然。

另一个变量是硬件。NVMe 的普及把本地 fsync 压到几十微秒,使得 Δ 中“落盘”占比下降,“网络 RTT”与“调度”占比上升。这意味着未来半同步的优化重点将从磁盘转向网络与 CPU 调度:同 AZ 部署、内核旁路、CPU 绑核的收益会越来越显著。本文评述:性能优化存在“木桶效应”,当最短板从磁盘换成网络,旧的调参经验会迅速失效,必须重新测量。

七、总结

回到最初的问题:0.5ms 到 2-5ms 的延迟增量,值不值得付?本文的答案是——这不是一个技术问题,而是一个经济问题,技术只负责把成本算清楚。半同步的额外延迟由网络 RTT、从库落盘、主库唤醒、组提交对齐四段构成,其中网络 RTT 是物理下限,其余可通过部署与调参压缩;并发叠加后的非线性劣化源于等待队列与锁持有的正反馈,单纯加并发无法突破由 k 决定的吞吐天花板。

决策路径可以归纳为四步:其一,量化 RPO,确认是否真的需要“单机故障不丢”;其二,用可复现压测测出自身的 Δ 与吞吐拐点,而非引用他人数字;其三,按表、按路径、按时间做分级,把半同步的暴露面收敛到高 L 低 QPS 的核心写入;其四,建立可观测性,让退化行为显式可见、可控、可回滚。本文评述:一致性是有价格的商品,工程的价值在于按需购买,而非全量订阅。

展望未来,随着存算分离与共识协议的成熟,半同步的适用边界会继续收窄,但在相当长的时间内,它仍是 MySQL 生态中“低成本获得单机故障不丢”的最现实手段。理解它的成本结构,比记住它的开关位置更重要。

文章声明

本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

文中涉及的延迟量级、TPS 衰减比例等数值,除明确标注文献来源者外,均为工程经验区间或建模参考值,不同硬件、网络与版本下会有显著差异,请以自身环境的实测结果为准。

主要参考文献

  1. MySQL Documentation Team. MySQL 8.0 Reference Manual: Semisynchronous Replication. Oracle, 2024. 链接
  2. MySQL Documentation Team. MySQL 8.0 Reference Manual: Replication and Binary Log Options. Oracle, 2024. 链接
  3. Percona Engineering. Semi-Synchronous Replication in MySQL 8.0: Behavior and Tuning. Percona Blog, 2023. 链接
  4. Kopytov A. sysbench: A System Performance Benchmark. GitHub Repository, 2024. 链接
  5. Oracle. MySQL 8.0: Group Replication and Consensus-Based Replication. 2024. 链接
  6. Kleppmann M. Designing Data-Intensive Applications. O'Reilly Media, 2017.(第 5、9 章关于复制与一致性的经典论述)
  7. Bernstein P A, Hadzilacos V, Goodman N. Concurrency Control and Recovery in Database Systems. Addison-Wesley, 1987.(事务与恢复的理论基础)
  8. Coronel C, Morris S. Database Systems: Design, Implementation, and Management. Cengage, 2022.(复制与高可用章节)
  9. MySQL Performance Schema 文档. Replication Tables and Instrumentation. Oracle, 2024. 链接

说明:本文在检索与整理过程中参考了 MySQL 官方手册、Percona 技术博客、sysbench 官方仓库及数据库系统经典教材等公开资料,累计参考条目 60 余篇(含官方文档、技术博客、开源仓库与教材章节),其中近三年(2022–2025)资料占比超过 50%。文中所有延迟与吞吐数值,除标注来源者外,均为工程经验区间或基于公开资料的建模参考值,非单一实验数据;涉及压测方法的部分,数据预处理细节已在 3.2 节说明(剔除预热与收尾区间、采用分位数聚合)。

内容仅供学习参考。如需引用,请以原始文献为准。

全文约 12600 字 | 参考文献 60 余篇(主要 9 篇)

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷