多副本写入冲突如何解决?
简化版
多副本并发写先检测因果关系:旧版本可丢弃,新版本可覆盖,互不支配的并发版本才是真冲突。 解决方式包括 LWW、保留 siblings 后业务合并、状态机规则或 CRDT。 余额、库存等强约束数据通常应在写入时协调,不能事后随意合并。
详细版
版本向量可判断因果先后与并发;HLC 或业务 revision 可辅助排序和条件写,但标量值本身不能证明两个写是否并发。 LWW 简单但会静默丢写,且物理时钟漂移危险。 集合、计数等可使用满足交换、结合、幂等的 CRDT;订单状态需合法迁移。 合并结果生成支配所有父版本的新版本,墓碑过早清理会让删除数据复活。
完整版教学
一、冲突来源
Leaderless 多副本允许不同节点同时接受写。 网络分区让双方看不到彼此更新。 恢复后同一实体出现多个合法后继版本。
冲突解决要实现业务语义,而不是选看起来最新的值。
二、检测关系
| 关系 | 处理 |
|---|---|
| incoming 因果更新 | 接受 |
| incoming 因果更旧 | 忽略 |
| 版本内容相同 | 去重 |
| 互不支配 | 并发冲突 |
| 同版本不同内容 | 分叉告警 |
向量时钟能识别并发,标量时间通常不能。
三、LWW 边界
LWW 选择时间戳最大值,平局按固定节点 ID。 它容易收敛,但静默丢失另一个更新。 时钟快的旧写可能压过业务上的新写。 只适合允许任一写获胜的低风险数据。
四、业务合并
用户资料可按字段 owner 合并。 订单按状态机禁止 SHIPPED 回退 CREATED。 购物车可合并集合,但数量仍需明确语义。 余额应使用账本或协调写,不取两个快照最大值。
五、CRDT
merge(a,b):
commutative
associative
idempotent
G-Counter、OR-Set 适合特定语义。 CRDT 不是任意对象的自动转换,元数据和墓碑也有成本。
六、合并后版本
系统保留 siblings A 和 B。 合并结果 M 的版本必须支配 A、B。 否则父版本下一轮仍会被识别为并发。 合并操作带 operationId,重复执行结果一致。
七、删除与观测
删除写入带因果版本的 tombstone。 所有旧副本修复前不能清除。 监控冲突率、siblings 数、合并失败和墓碑年龄。 冲突突增常提示网络分区、路由错误或热点实体。
八、常见误区与追问
- 误区:时间戳最大就是正确最新。 时钟与业务顺序可能不同。
- 误区:冲突都能自动合并。 强约束对象常需协调。
- 误区:CRDT 没有代价。 元数据和业务模型受限。
- 误区:合并保留任一父版本即可。 新版本必须支配全部父版本。
- 追问:余额如何处理? 用不可变账本和唯一操作。
- 追问:为何保存 siblings? 防静默丢写并保留证据。
- 追问:删除为何复活? 墓碑早于旧副本恢复被清理。
九、加强记忆
- 区分旧、新、重复和并发。
- LWW 简单但会丢写。
- 状态机和字段 owner 决定语义。
- CRDT 只适合可合并类型。
- 合并版本支配父版本,墓碑保护删除。