← 返回题目列表

分布式写入冲突如何发现?

中等 第 17 / 25 题 更新于 2026/10/04
写冲突乐观锁版本向量CAS
学习 AI 实战项目

简化版

写冲突检测要让请求声明“基于哪个版本修改”,系统判断当前版本是否仍匹配。 单主常用 version CAS/ETag;多主用版本向量区分因果与并发,HLC 可辅助排序但不能单独识别并发;唯一约束和状态机发现业务冲突。 只看更新时间或失败日志会漏掉静默覆盖。

详细版

客户端读取 v12 后提交 expectedVersion=12,存储用主键和版本做条件更新。 影响行数为 0 表示已有并发写或对象不存在,返回冲突并让客户端重读。 多副本各自 version+1 会生成同号不同内容,需要因果版本、中心序列或共识。 冲突还包括唯一键、库存不为负、状态非法等不变量。

完整版教学

一、丢失更新

A、B 都读取版本 5。 A 写入 X,B 随后写入 Y。 没有条件时 Y 静默覆盖 X。

冲突检测要把读取时的前置版本带回写入。

二、乐观锁

UPDATE item
SET value = :newValue, version = version + 1
WHERE id = :id AND version = :expectedVersion;

一行受影响表示成功,零行表示冲突或不存在。 再次查询可区分原因。

三、HTTP ETag

GET 返回 ETag v12。 更新使用 If-Match v12。 不匹配返回 412 Precondition Failed。 ETag 要绑定实体语义版本,不用不稳定序列化结果。

四、多主并发

两个副本都从 v5 写出 v6,标量无法判断。 版本向量可识别互不支配。 HLC 辅助排序,但业务仍需解决冲突。 需要唯一顺序时使用 Leader 或共识。

五、业务不变量

唯一索引发现重复用户名。 库存使用 quantity>=amount 的条件更新。 订单使用 expectedState 防非法迁移。 行版本不能替代这些业务约束。

六、冲突处理

冲突类型可选处理不能忽略的边界
单记录覆盖CAS 失败后重读、重算、重试重试函数必须基于新状态
可交换增量原子累加或合适的 CRDT操作必须满足对应代数性质
字段归属不同按字段权威来源合并不能用整行时间戳覆盖
资金或库存不变量冲突拒绝并进入人工/业务流程不能靠 LWW 静默吞掉

返回 409/412 让客户端重读和合并。 可交换操作可基于新状态重算。 不可合并版本进入人工队列。 重试使用相同 idempotencyKey 防重复副作用。

七、观测审计

统计按实体、接口和原因的 conflict rate。 记录 expected/current version、字段摘要和 requestId。 热点冲突高时考虑命令化、分片或串行化。 测试覆盖并发屏障、乱序与响应丢失。

八、常见误区与追问

  • 误区:事务会自动发现所有丢更新。 取决于隔离级别与写法。
  • 误区:更新时间可可靠检测。 精度与漂移会失败。
  • 误区:CAS 失败后直接重放。 必须基于新状态重算。
  • 误区:行版本表达所有冲突。 业务不变量仍需检查。
  • 追问:多主为何不能各自加一? 会生成同版本不同内容。
  • 追问:冲突返回什么? 当前版本和重试指引。
  • 追问:热点怎么办? 改命令语义、分片或共识串行化。

九、加强记忆

  1. 写携带 expectedVersion。
  2. CAS/ETag 暴露静默覆盖。
  3. 多主用因果版本或共识。
  4. 业务约束独立检查。
  5. 冲突后重读再决策。