尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

高并发流量治理实战(8):读写分离与主从延迟治理:从路由到业务兜底

高并发流量治理实战(8):读写分离与主从延迟治理:从路由到业务兜底 换个场景内容社区的发出去的文章不见了上一篇库存服务的对账实验里我们默认读 DB读的是同一个 DB——现实里高并发系统的读压大部分被路由到了从库于是出现裂缝的另一半作者 15:00:00 发布文章DB 提交成功自己刷新个人主页却看不到10 秒后运营在后台也看不到判定发布失败又点了一次发布。这就是主从延迟治理要接的锅。读写分离本身不难——难的是把从库可能旧多久变成一个有界、可观测、业务可预期的量。本篇分两刀第一刀切路由哪些读必须回主库、哪些可以等位点第二刀切容量延迟是怎么从复制拓扑问题变成回放速率问题的。路由层的三条军规现代接入层ProxySQL、RDS 代理、ShardingSphere 类客户端做读写分离规则都是三条的变体事务内的读永远走主自己没提交的更改只有自己看得见写后短时间内该会话的读走主会话粘性治读己之写其余只读流量走从接受有界陈旧。第二条是事故最多发的一条因为短时间通常被写成一个常数——写后 5 秒读主。问题是 5 秒的假设对手是变化的延迟复制积压 1 秒时它绰绰有余积压 12 秒时它一触即溃。把三条策略放到同一段写高峰期 lag 爬升的虚拟时间轴上对比无脑从库 / 写后粘主 5 秒 / 按位点等待等不起就降级读主# 作者 150s 发布文章(DB 提交于 t150). 从库可见性 t - lag(t)# 写高峰期 lag 线性爬升, 之后回落: 三段确定性折线deflag(t):ift100:return2.0ift200:return2.00.1*(t-100)returnmax(2.0,12.0-0.05*(t-200))COMMIT,OFFSETS150,[1,2.5,5,6.5,12,50]defp1(t_r):# 无脑从库ok(t_r-lag(t_r))COMMITreturnSLAVE,OKifokelse读不到!defp2(t_r,sticky5):# 写后 5 秒会话粘主ift_rCOMMITsticky:returnMASTER,OKreturnp1(t_r)defp3(t_r,cap4.0):# 按位点等待(上限 cap 秒), 超时降级读主tt_rwhilet-lag(t)COMMIT:t0.5waitt-t_rifwaitcap:returnSLAVE(等%.1fs)%wait,OKreturnMASTER(降级),OKprint(写入提交 t%d, 从库 lag 随写高峰在 t200 前爬升到 12s%COMMIT)print(%-6s %-6s | %-12s %-8s | %-12s %-8s | %s%(读时刻,lag,策略1 从库,结果,策略2 粘主5s,结果,策略3 位点等待))v1v2m2m30rows[]foroffinOFFSETS:t_rCOMMIToff r1,o1p1(t_r)r2,o2p2(t_r)r3,o3p3(t_r)rows.append((t_r,r1,o1,r2,o2,r3,o3))fort_r,r1,o1,r2,o2,r3,o3inrows:v1o1!OKv2o2!OKm2MASTERinr2 m3MASTERinr3print(t%5.1f %5.1fs | %-12s %-8s | %-12s %-8s | %s%(t_r,lag(t_r),r1,o1,r2,o2,r3))print(读不到次数: 策略1%d 策略2%d 策略30 | 主库承担的读: 2%d 3%d (共6次读)%(v1,v2,m2,m3))运行输出写入提交 t150, 从库 lag 随写高峰在 t200 前爬升到 12s 读时刻 lag | 策略1 从库 结果 | 策略2 粘主5s 结果 | 策略3 位点等待 t151.0 7.1s | SLAVE 读不到! | MASTER OK | MASTER(降级) t152.5 7.2s | SLAVE 读不到! | MASTER OK | MASTER(降级) t155.0 7.5s | SLAVE 读不到! | MASTER OK | SLAVE(等3.0s) t156.5 7.7s | SLAVE 读不到! | SLAVE 读不到! | SLAVE(等1.5s) t162.0 8.2s | SLAVE OK | SLAVE OK | SLAVE(等0.0s) t200.0 12.0s | SLAVE OK | SLAVE OK | SLAVE(等0.0s) 读不到次数: 策略14 策略21 策略30 | 主库承担的读: 23 32 (共6次读)注模拟步长 0.5 秒等待 2.5 与 3.0 显示为同类不影响结论。三行结论自己走出来策略 1 四次踩空证明延迟非零即事故策略 2 在 t156.5 破防——粘主窗口 5 秒到期时 lag 还有 7.7 秒固定粘主时长只在lag 窗口的世界里成立而这个世界并不存在策略 3 把读己之写彻底治好代价是两次小延迟3 秒内和两次降级读主主库多扛 2/6 的读。位点等待在 MySQL 里就是SELECT WAIT_FOR_EXECUTED_GTID_SET(uuid:seq, timeout)或 GTID 集对比gtid_executed写入成功后把位点存进会话Redis带 TTL此后的强一致读先问从库追到位点了吗追得上就等、追不上就回主。它是三者中唯一语义正确且自适应延迟波动的前提是你的复制位点是单调可比较的——这也是 GTID 模式相对文件位点模式在治理上的真实价值。容量层延迟不是玄学是回放速率的账大多数团队把主从延迟归因于网络慢/磁盘慢但生产事故里两个最常见的真凶都在回放速率上其一是旧版本单线程 SQL 线程按序回放主库并发的写洪峰到从库排队泄洪其二是大事务——一条 UPDATE 改 500 万行的语句在主库执行 40 秒其 binlog 要在提交后一次性传给从库回放从库再执行几十秒瞬时 lag 直接跳到分钟级8.0 的并行回放/MTS 按库表甚至 WRITESET 并行度缓解了其一治不了其二。用积压 写入事件速率 − 回放能力的积分模型看三种画像# 从库回放能力模型: 220 事件/秒; 三种上游写入画像, 300 秒虚拟回放CAPACITY220.0defreplay(name,backlog0,inflow_fn,seconds300):backlog,peak,drain_atbacklog0,backlog0,Noneforsinrange(seconds):backlogmax(0.0,backloginflow_fn(s)-CAPACITY)peakmax(peak,backlog)ifs30anddrain_atisNoneandbacklogbacklog01:drain_atsprint(%-28s 峰值延迟 %5.1fs | 积压峰值 %6.0f 事件 | 回基线耗时 %s%(name,peak/CAPACITY,peak,(%ds%drain_at)ifdrain_atelse窗口内未回))defconst(v):returnlambdas:vdefburst(start,end,v,base):returnlambdas:vifstartsendelsebaseprint(从库回放上限 %d 事件/秒, 初始积压 %d 事件%(CAPACITY,220))replay(日常流量 180/s,220,const(180))replay(大促开闸 400/s 持续60s,220,burst(0,60,400,180))replay(无锁DDL 瞬发6000事件/10s,220,burst(0,10,600,180))replay(双11零点 500/s 持续120s,220,burst(0,120,500,180))运行输出从库回放上限 220 事件/秒, 初始积压 220 事件 日常流量 180/s 峰值延迟 1.0s | 积压峰值 220 事件 | 回基线耗时 31s 大促开闸 400/s 持续60s 峰值延迟 50.1s | 积压峰值 11020 事件 | 回基线耗时 窗口内未回 无锁DDL 瞬发6000事件/10s 峰值延迟 18.3s | 积压峰值 4020 事件 | 回基线耗时 104s 双11零点 500/s 持续120s 峰值延迟 153.7s | 积压峰值 33820 事件 | 回基线耗时 窗口内未回模型给出的治理清单非常具体持续性超容量后两行不是延迟治理问题是拓扑问题——写洪峰速率超过单机回放上限时唯一解是把读流量从这套主从中剥离扩容从库只分流读、不解决回放要解决复制本身得上多分片写或级联复制脉冲型积压DDL 行则是调度问题——6000 事件的瞬时突刺带来 18 秒峰值、104 秒回血所以错峰执行 DDL/大事务拆小批每批几千行 批间 sleep不是 DBA 的洁癖是在给所有依赖 lag 上界的策略包括上面的位点等待发保底工资。还有一个隐藏结论粘主窗口、位点等待超时 cap 这些参数都应引用 lag 的实测峰值分位动态调整而不是写死——t156.5 那次破防根源就是把动态量当常数。业务兜底路由治不了的产品来治最后划清边界位点等待保的是读己之写而作者 A 发文、粉丝 B 立刻搜到属于跨会话新鲜度任何异步复制都给不了硬保证只能业务侧兜底发布成功后内容双写进搜索/信息流通道消息驱动绕过复制个人主页读自己的作品列表强制走主给粉丝展示作者大大刚刚在编辑的软提示替代空白。这类设计的共同点是把一致性承诺从存储层上移到交互层用信息透明换技术豁免。路由与延迟的账算完了但所有参数都建立在我们知道系统容量在哪的前提上——这个前提平时是信仰只有压测能把它变成数字。下一篇《高并发流量治理实战9全链路压测方法容量评估、影子表与压测标透传》讲怎么不打扰真实用户地把整条链路的底摸清。参考来源MySQL 8.0 Reference Manual: Replication主从复制与并行复制: https://dev.mysql.com/doc/refman/8.0/en/replication.htmlMySQL 8.0 Reference Manual: WAIT_FOR_EXECUTED_GTID_SET(): https://dev.mysql.com/doc/refman/8.0/en/gtid-functions.htmlMySQL 8.0 Reference Manual: Multi-Threaded Replication: https://dev.mysql.com/doc/refman/8.0/en/replication-multi-threaded-appliers.htmlWikipedia: Read–write splitting: https://en.wikipedia.org/wiki/Read%E2%80%93write_splittingtags: 读写分离, 主从延迟, MySQL, 复制本系列已结集为免费专栏《高并发流量治理实战从限流到全链路压测》 https://blog.csdn.net/weixin_67153745/category_13213830.html 系统性进阶推荐付费专栏《提示词工程实战从入门到生产级 Prompt 设计》限时 ¥19.9首篇免费试读https://blog.csdn.net/weixin_67153745/category_13213600.html
返回列表