4. 案例1
在读写分离架构中,订单写入主库、查询走从库是常见做法。下单成功后立即跳转订单详情页,页面却提示“订单不存在”,而稍后刷新又能看到——这类“ID缺失”往往不是订单真的丢了,而是从库还没追上主库的写入。本节围绕这一典型现象,讲清它的成因、如何用监控数据确认,以及如何用强制读主策略稳定排障。
现象还原:为什么“刚下的单”查不到
假设订单服务采用一主两从部署,写请求路由到主库,读请求按权重分发到两个从库。用户提交订单后,主库完成 INSERT 并返回自增订单ID(例如 202406180001237),前端随即携带该ID请求 GET /order/{id}。此时读请求被路由到从库B,而从库B的 SQL 线程还在回放几十毫秒前的 binlog,订单行尚未可见,查询返回空结果,接口便抛出“订单不存在”。
关键点在于:写入成功与读取可见之间存在时间差。主从复制是异步的(或半同步但仍有回放延迟),从库的数据版本落后于主库。只要“写后立即读”的读请求落到延迟中的从库,就会出现ID缺失。它与订单ID生成重复、分库分表路由错误、缓存穿透等问题的表现相似,但根因完全不同,必须先用数据区分。
用监控数据确认延迟,而不是凭感觉
排障第一步是把“怀疑”变成“证据”。在 MySQL 主从架构中,可在从库执行 SHOW SLAVE STATUS\G,重点看三个字段:Slave_IO_Running 与 Slave_SQL_Running 是否均为 Yes,以及 Seconds_Behind_Master 的数值。若 SQL 线程正常但该值在 0.1~2 秒间波动,说明复制链路健康、只是回放滞后,属于典型的延迟型缺失;若该值为 NULL 或线程为 No,则要转向复制中断排查,性质不同。
更精细的做法是对比位点。在主库执行 SHOW MASTER STATUS 记录当前 File 与 Position,再到从库查看 Relay_Master_Log_File 与 Exec_Master_Log_Pos,两者差距即“尚未回放的事务量”。结合业务侧埋点,可以算出真实影响:例如在订单创建后记录主库提交时间戳,在详情查询处记录读库返回时间,若两者差值集中在 50~400ms 且查询为空,就基本锁定延迟窗口。示例中 Seconds_Behind_Master 约 0.3 秒、订单创建到查询间隔约 120ms,正好落在窗口内,与现象吻合。
强制读主:把“写后读”钉在主库上
确认根因后,最直接的修复是让“下单后立即查询”这类强一致读走主库。工程上常用三种落地方式,可组合使用:
- 请求级强制读主:在创建订单的响应中带上标记(如响应头
X-Read-Master: 1或会话标记),详情接口识别到标记后将该次查询路由到主库,仅对“刚写完”的短窗口生效,避免全量读压到主库。 - 按ID时间窗判断:订单ID含时间信息时,若当前时间与ID生成时间差小于阈值(如 1 秒),则读主库;超过阈值再走从库。阈值应大于实测 P99 延迟,示例中可设为 1s。
- 会话粘滞:同一用户会话在写操作后的一小段时间内固定读主库,适合“下单→支付→查看”连续操作场景。
以请求级方案为例,路由层可这样判断(伪代码,参数为示例假设):
// 读路由决策(示例)
boolean needMaster = request.hasHeader("X-Read-Master")
|| (orderId != null && System.currentTimeMillis() - extractTime(orderId) < 1000L);
DataSource ds = needMaster ? masterDataSource : slaveLoadBalancer.pick();
需要注意,强制读主是“收敛影响面”的手段,不是掩盖延迟的借口。若 Seconds_Behind_Master 长期偏高(如持续大于 5 秒),说明从库回放能力不足、大事务或锁等待拖慢 SQL 线程,此时应优化大事务拆分、提升从库规格或调整并行复制参数,而不是无限扩大读主范围,否则主库读压力会反噬写入性能。
验证与回归:怎么确认真的修好了
修复后要形成可观测的闭环。建议在详情接口增加两个指标:order_read_master_total(读主次数)与 order_not_found_total(查无此单次数),并按“订单创建后 1 秒内查询”维度单独统计。验证时构造连续下单并立即查询的压测流量(示例:50 并发、持续 3 分钟),观察“查无此单”计数应从修复前的明显非零降为 0,同时主库 QPS 增量应控制在可接受范围(示例中读主占比约 8%)。若仍有零星缺失,再检查是否存在多从库间延迟不均、连接池复用旧连接读到其他从库等情况。
本案例的排障主线可以概括为:先用 SHOW SLAVE STATUS 与位点差确认延迟存在,再用“重查是否恢复”区分延迟型与其他型缺失,最后用请求标记或时间窗把强一致读收敛到主库,并以“查无此单”指标归零作为验收依据。掌握这条链路,后续遇到缓存与主从叠加、分库路由与延迟交织的复合场景,也能按同样的证据顺序逐层剥离。