在安塔利亚 Belek 一场 1200 人份的宴会上,4 位服务员同时向同一张 20 人圆桌添加菜品。经典的 "last-write-wins" 会静默覆盖 14 道菜。下面介绍我们如何把乐观锁、409 Conflict 和重试模式落到生产。
为什么 last-write-wins 不够用
朴素的 UPDATE orders SET items = ? WHERE id = ? 只保留最后一次 commit。200 毫秒内 4 个 POST 进来,厨房只看到 6 道菜。
方案:每行加一列 version。客户端打开订单时读取,提交时回传,服务器做原子校验。
基于版本号的乐观锁
新增 version INTEGER 与 updated_at。PATCH 流程:
- 客户端:
{ items, expected_version: 7 }。 - 服务器:
UPDATE ... WHERE id = ? AND version = 7。 - 影响 0 行 →
409 Conflict+ 当前状态。
409 Conflict + 客户端合并
不做自动重试。客户端显示最新订单,高亮要新增/移除的项,等待用户确认。Belek 现场感知冲突率从 0.4% 降到 0%。
再叠加 append-only 的 order_events 日志,谁、什么时间、做了什么都可审计。
常见问题
为什么不用 SELECT FOR UPDATE?它会序列化服务员,影响吞吐。
在 D1 上可用吗?可以,基于 INTEGER 的原子 UPDATE 不触发反模式。
离线呢?本地队列 + expected_version,重连时进入合并界面。
觉得有用?分享给朋友。