客户下单后商品可售量下降,几秒后页面又显示旧库存,再刷新才恢复。接入数据库读写分离后,这可能是写入主库成功、随后的读取被路由到复制延迟的副本。对库存、订单和客户会话,读后写一致性比普通目录查询更敏感。

先测延迟,不凭感觉

使用数据库平台指标或复制状态记录 seconds/GTID lag、回放队列和 I/O。应用日志给一次请求加 correlation ID,标明读写连接与关键实体版本。只测网络 ping 不能代表事务已复制。

哪些读取必须回主库

提交订单后的确认、支付状态、可售性校验、刚保存的客户地址通常需要 read-after-write。路由层应在事务和短期一致性窗口内保持主库读取,不能随机把每条 SELECT 分散到副本。

MSI 与缓存会叠加

reservation 写入数据库,可售量又可能经过库存索引和页面缓存。即使副本同步,FPC/CDN 仍可能显示旧提示;反过来,绕过缓存仍读副本也会旧。分别记录数据库版本、索引更新时间和响应 cache status。

副本不应承担不兼容查询

长报表在副本运行会拖慢 SQL thread 回放,制造更大 lag。监控副本 CPU、I/O、长事务和锁;必要时把报表副本与在线读取副本分开。故障切换前确认候选副本已追平,否则提升会丢失最近交易。

验证一致性边界

测试下单、取消、退款、库存导入和高并发最后库存;每次立即读取与延迟读取都记录连接。允许最终一致的目录展示应有清晰窗口,但结账最终校验必须在权威数据上。修复不能只靠强制所有读走主库,应在正确性与扩展性之间明确路由。

事务粘滞的实现边界

“写后若干秒读主库”需要以请求/会话或实体版本为依据,不能全站固定等待。后台批量任务可能在一个流程中多次提交并读取,连接路由要理解事务结束和新事务。ORM 层若缓存实体,也可能返回旧对象,排查时要区分应用缓存与副本。

监控复制位点

比秒数更可靠的是写入位点与副本回放位点。关键流程可以等待副本追到已知 GTID/LSN 后再读,但必须有短超时和主库回退,不能让结账无限等待。具体能力取决于数据库与代理。

复制过滤和 schema 差异

副本缺表、列、触发器或过滤了某些数据库时,表现不只是延迟,而是永久不一致。部署 schema 后核对复制错误与表结构,再把副本加入读池。读副本必须在应用版本切换前兼容新查询。

故障演练

模拟副本延迟、断开和主从切换,观察应用是否自动摘除不健康节点、是否把写错误路由到只读库。库存正确只是结果之一,还要确认订单、客户与配置读取没有回退到旧版本。

用版本号识别旧读

关键库存记录可在诊断环境附带更新时间、reservation 最新 ID 或业务版本,应用响应也记录读取节点。发现页面数值回退时,对比版本即可证明是否从新值读回旧值。不要把这些内部字段直接暴露给客户,日志同样要限制权限。

不要让健康检查只验证连接成功

副本能接受 SELECT 不代表可用于交易读取。读池健康检查应包含复制线程状态、延迟/位点、只读属性和最近错误;超过阈值就摘除,并把流量回退到可用节点。恢复加入前要持续观察一段时间,防止节点反复抖动。

隔离一次完整下单链路

在预发布给测试 SKU 设置已知库存,记录下单前后 source item、reservation、salable quantity、订单状态与每次读取节点。再人为制造可控延迟,验证结账最终校验走权威数据、目录展示在允许窗口内收敛,取消订单也能正确补偿。