冲突解决
多主复制中的冲突解决
多领导者复制的最大问题是可能发生写冲突,这意味着需要解决冲突。例如,两个写操作并发地修改了同一条记录中的同一个字段,并将其设置为两个不同的值。考虑一个由两个用户同时编辑的维基页面,如下图所示。用户

其他类型的冲突可能更为微妙,难以发现。例如,考虑一个会议室预订系统:它记录谁订了哪个时间段的哪个房间。应用需要确保每个房间只有一组人同时预定(即不得有相同房间的重叠预订
同步与异步冲突检测
在单主数据库中,第二个写入将被阻塞,并等待第一个写入完成,或中止第二个写入事务,强制用户重试。另一方面,在多活配置中,两个写入都是成功的,并且在稍后的时间点仅仅异步地检测到冲突。那时要求用户解决冲突可能为时已晚。
原则上,可以使冲突检测同步
避免冲突
处理冲突的最简单的策略就是避免它们:如果应用程序可以确保特定记录的所有写入都通过同一个领导者,那么冲突就不会发生。由于多领导者复制处理的许多实现冲突相当不好,避免冲突是一个经常推荐的方法。
例如,在用户可以编辑自己的数据的应用程序中,可以确保来自特定用户的请求始终路由到同一数据中心,并使用该数据中心的领导者进行读写。不同的用户可能有不同的“家庭”数据中心(可能根据用户的地理位置选择
但是,有时您可能需要更改指定的记录的主库——可能是因为一个数据中心出现故障,您需要将流量重新路由到另一个数据中心,或者可能是因为用户已经迁移到另一个位置,现在更接近不同的数据中心。在这种情况下,冲突避免会中断,你必须处理不同主库同时写入的可能性。
收敛至一致的状态
单主数据库按顺序应用写操作:如果同一个字段有多个更新,则最后一个写操作将确定该字段的最终值。在多主配置中,写入顺序没有定义,所以最终值应该是什么并不清楚。在标题修改的例子中,在主库
如果每个副本只是按照它看到写入的顺序写入,那么数据库最终将处于不一致的状态:最终值将是在主库
实现冲突合并解决有多种途径:
- 给每个写入一个唯一的
ID (例如,一个时间戳,一个长的随机数,一个UUID 或者一个键和值的哈希) ,挑选最高ID 的写入作为胜利者,并丢弃其他写入。如果使用时间戳,这种技术被称为最后写入胜利(LWW, last write wins) 。虽然这种方法很流行,但是很容易造成数据丢失。 - 为每个副本分配一个唯一的
ID ,ID 编号更高的写入具有更高的优先级。这种方法也意味着数据丢失。 - 以某种方式将这些值合并在一起
- 例如,按字母顺序排序,然后连接它们。 - 在保留所有信息的显式数据结构中记录冲突,并编写解决冲突的应用程序代码(也许通过提示用户的方式
) 。
自定义冲突解决逻辑
作为解决冲突最合适的方法可能取决于应用程序,大多数多主复制工具允许使用应用程序代码编写冲突解决逻辑。该代码可以在写入或读取时执行。
写时执行
只要数据库系统检测到复制更改日志中存在冲突,就会调用冲突处理程序。例如,
读时执行
当检测到冲突时,所有冲突写入被存储。下一次读取数据时,会将这些多个版本的数据返回给应用程序。应用程序可能会提示用户或自动解决冲突,并将结果写回数据库。例如,