知识卡片
双写的竞态条件问题引出变更数据捕获的动机
内容
现实系统通常要同时维护数据库、缓存、搜索索引、数据仓库好几份数据副本,各自为不同目的优化存储方式,彼此间必须保持同步。若嫌完整数据转储(批处理ETL)太慢,一个常见但危险的替代方案是双写:应用代码在数据变更时显式依次写多个系统(先写数据库、再更新搜索索引)。双写有两个致命问题:一是竞态条件——两个客户端并发更新同一项,如果两边写入两个系统的时序恰好交错(数据库先看到客户端1后看到客户端2、搜索索引却反过来先看到客户端2后看到客户端1),两个系统会在没有任何报错的情况下永久性地存下不同的最终值,且没有额外的并发检测机制(如版本向量)你甚至发现不了这个问题发生过;二是原子性问题——两次写入里一次成功一次失败,这本质就是[[原子提交问题与两阶段提交的机制及两个不归路]]里讨论过的原子提交问题,代价高昂难以低成本解决。根本症结在于双写场景里没有唯一的领导者来确定写入顺序——数据库和搜索索引可能各自都有自己的主库,却互不追随对方。解法的方向由此浮现:如果能让其中一个系统(如数据库)真正成为唯一领导者,让另一个系统(搜索索引)变成它的追随者,问题就能被状态机复制的思路解决——这正是变更数据捕获要解决的问题。
参考来源
- 位置:《数据密集型应用系统设计》第十一章《流处理》"保持系统同步"(源文件:_epub-src/ch11_split_001.html)
- 结论依据:原文用两个客户端并发更新数据库与搜索索引、写入顺序交错导致两系统最终值不一致的具体例子说明双写的竞态问题,并指出双写还存在原子提交式的部分失败风险,根源是没有单一领导者决定写入顺序,直接支撑本卡片结论。
- 原始内容:因为运气不好,这些请求的时序是交错的……这两个系统现在也永久地不一致了……如果实际上只有一个领导者……而且我们能让搜索索引成为数据库的追随者,情况要好得多。