UPDATE orders SET status='paid', version=version+1 WHERE id=1001 AND version=7;

支付成功和退款两条回调几乎同时到达,最终订单状态是待支付,update 时间显示后写的是退款那条。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,两个消费者各自读出订单再整行 update,后提交的用旧快照覆盖了前一个的结果。没有版本号,也没有状态机校验。

围绕「回调并发状态覆盖」排查时,命令行跑一遍比在面板里点十次有用,面板会替你做掉不少隐式操作。动手前把原文件复制一份带时间戳,出问题立刻换回去,比现场分析快得多。

一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

处理上按这个顺序:状态更新带上版本号 where version 等于读到的值;或者用带前置状态的条件 update;非法流转直接丢弃并记日志;用并发脚本同时打两条回调验证。

改完只确认一件事:并发回放后订单停在正确终态 影响行数为 0 的那次被记录为非法流转。

作者 9823189