如何解决CouchDB数据库服务器与本地同步时出现的冲突问题
CouchDB 双向同步文档回退、冲突问题解决方案
问题触发原因
导致部分集合异常回退的核心原因是CouchDB的默认冲突判定逻辑和同步异常共同作用,具体可分为三类:
- 默认获胜版本规则和时间无关:CouchDB不会按更新时间判定冲突的获胜版本,默认选择
_rev字段字母序更大的版本作为胜出版本。如果旧版本的修订分支生成了更长的修订链,或者_rev的随机后缀字母序更高,旧版本就会被判定为有效版本,表现为文档“回退”。 - 部分节点囤积未同步的旧修订:如果双向同步配置了过滤规则,或者某一端的同步任务出现过中断、重试异常,就可能导致部分节点长期囤积未同步的旧版本修订。等同步恢复后这些旧修订参与冲突判定,仅会影响涉及到这些旧修订的集合,其余文档不受影响,符合仅部分集合异常的特征。
- 高写入集合冲突分支过多:出现问题的集合如果写入频率远高于其余文档,双向同步过程中更容易产生多分支的修订链,当分支数量超过CouchDB默认的冲突保留上限后,部分新的修订分支可能被误清理,最终旧版本成为唯一留存的有效分支。
自动选择最新版本作为获胜版的实现方法
CouchDB没有提供开箱即用的按时间自动选择获胜版本的API,你可以通过以下两种方案实现需求:
方案1:自定义定时冲突处理脚本(适配现有架构)
- 首先要求所有写入操作必须在文档中增加
last_updated毫秒级时间戳字段,作为版本新旧的判定依据 - 定期遍历数据库的冲突文档:调用
GET /{db}/_changes?filter=_conflicts接口可以直接拉取全库所有存在冲突的文档ID - 对每个冲突文档,调用
GET /{db}/{doc_id}?conflicts=true获取所有冲突的修订号,再批量拉取所有冲突修订的完整内容 - 对比所有冲突修订的
last_updated值,保留时间最新的版本,调用DELETE /{db}/{doc_id}?rev={old_rev}接口删除其余所有冲突的旧修订分支即可。
你可以把上述逻辑封装为定时任务,或者绑定到同步任务的完成钩子上,每次同步结束后自动执行。
方案2:调整同步规则从根源减少冲突
如果业务场景允许,对高冲突的集合可以采用单主写入模式:所有更新操作统一请求主节点执行,其余节点仅作为只读副本,双向同步仅同步主节点生成的修订,从根源上避免多分支修订产生,不需要额外做冲突处理。
内容的提问来源于stack exchange,提问作者user16412923
相关产品推荐
相关产品推荐

