知识卡片

写冲突的检测时机与收敛策略最后写入胜利的数据丢失代价

普通读书笔记卡

内容

[[多主复制的三大适用场景本质都是容忍长时间断连]]带来的最大问题是写冲突:两个用户 同时在不同主库上把同一条记录改成不同值,单主数据库里第二个写入会被阻塞或直接冲突 中止,但多主配置下两个写入都会先各自成功、只是稍后异步检测到冲突,这时可能已经 太晚,让用户当场解决已经不现实。原则上可以让冲突检测同步化(等写入复制到所有副本 再告诉用户成功),但这样做等于放弃了多主复制”每个副本独立接受写入”的核心优势—— 真要同步检测,不如直接用单主复制。最简单的处理策略是从源头避免冲突:如果应用能 保证特定记录的所有写入都走同一个领导者(比如按用户ID路由到固定的”家”数据中心), 冲突就根本不会发生,这也是很多多主复制实现在冲突处理上做得不好、因此被经常推荐的 方法——但用户迁移或数据中心故障导致路由改变时,这个前提会被打破。如果无法避免 冲突,就必须靠某种规则让所有副本最终收敛到相同值,因为单主数据库天然有”最后一次 写入决定最终值”的顺序,多主配置下没有这种天然顺序,必须人为定义收敛规则:给每个 写入一个唯一ID(时间戳、随机数、UUID或哈希),选ID最大的作为胜者、丢弃其余—— 如果用时间戳,这就是”最后写入胜利”(LWW);给每个副本分配唯一ID、按副本ID优先级 决定胜者;把值合并(比如按字母序拼接);或者用能保留所有信息的显式数据结构记录 冲突、交给应用代码(甚至用户)来解决。LWW虽然流行且容易实现最终收敛,但代价是 悄悄丢数据——只要同一个键存在并发写入,即使每个写入都被数据库确认成功,最终也只有 一个能存活,其余被静默丢弃,如果数据丢失不可接受,LWW是很糟糕的选择。

参考来源

- 位置:《数据密集型应用系统设计》第五章《复制》"处理写入冲突""收敛至一致的状态" (源文件:_epub-src/ch5_split_003.html) - 结论依据:原文说明多主配置下冲突是异步检测的、同步检测会失去多主优势,避免 冲突靠固定路由到同一领导者,收敛需要人为定义规则(唯一ID选胜者/副本优先级/ 合并/自定义逻辑),并说明LWW以持久性为代价实现最终收敛,直接支撑本卡片结论。 - 原始内容:在多活配置中,两个写入都是成功的,并且在稍后的时间点仅仅异步地检测 到冲突……给每个写入一个唯一的ID……挑选最高ID的写入作为胜利者,并丢弃其他写入。 如果使用时间戳,这种技术被称为最后写入胜利……很容易造成数据丢失。