用户取消订单后立刻重新下单,从库还显示已取消,应用据此 update 订单状态,锁住了主库上另一笔正在支付的单,等待 12 秒后超时。日志里能对上时间点,这种反而好排。
读走从库拿到的状态比主库旧,应用基于旧状态拼出的条件在主库上匹配到了不该动的行。锁加在错误的数据上,冲突对象也跟着错。延迟越大,这种错配越容易出现。这一点先确认清楚,后面就不用反复试。
SHOW SLAVE STATUS\G SELECT id, status, version FROM orders WHERE id = 9001;
「从库旧值加锁」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。偶发问题先统计频率,一小时几次和一天一次,查法完全不同。
处理上按这个顺序:写后读强制走主库 至少同一订单保持不变;update 条件里带上状态和版本 不匹配时影响行数为 0;监控 Seconds_Behind_Master 超过阈值就切主库读;关键流程不要用从库结果做写决策。
结果对不对很好判断,取消后重下的链路不再出现跨订单锁等待 update 影响行数与预期一致。
