数据写入双数据库引擎时保障最终一致性的成熟方案有哪些?
双写Mongo与搜索引擎最终一致性落地解决方案
下面是工业界已经经过验证的成熟落地方案,均不依赖连接器、消息队列等额外中间件,也不需要引入跨异构存储的分布式事务:
1. 同步状态标记+定时补偿巡检方案
这是适配性最高的通用方案:
- 调整写入流程:在Mongo的业务文档中新增
es_sync_status字段,取值为pending(待同步)、success(同步成功)、failed(同步失败)。利用Mongo单文档写入的原子性,先把业务数据+es_sync_status: pending一次性写入Mongo,写入成功后再执行ES写入操作;ES写入成功后,再把Mongo中对应文档的同步状态更新为success。 - 新增兜底巡检任务:后台启动轻量定时任务,按照业务可接受的一致性延迟设置执行间隔(通常为1~10分钟),扫描Mongo中
es_sync_status为pending、且创建时间大于写入超时阈值(比如5分钟,排除正处于写入流程中的数据)的文档,挨个重试写入ES;重试成功则更新状态为success,重试超过预设次数(比如5次)则标记为failed触发告警人工介入。 - 优势:逻辑简单无额外依赖,数据一致性可控,适合绝大多数中小规模写入场景。
2. 本地事件表方案
适合写入量较大、业务逻辑复杂的场景:
- 写入流程调整:在Mongo中新增独立的
es_sync_events集合,用于存储需要同步到ES的操作事件,事件包含唯一ID、操作类型、业务数据、版本号、同步状态字段。利用Mongo的批量原子写入能力,把业务数据写入和事件写入放在同一个批量操作中执行,保证只要业务数据写入成功,对应的同步事件就一定存在。 - 事件消费逻辑:在服务本地启动单线程异步消费进程,按事件生成顺序消费待处理的同步事件,写入ES成功后标记事件为已完成,写入失败则原地重试;如果出现不可重试错误则触发告警。
- 优势:业务逻辑和同步逻辑完全解耦,通过事件的版本号可以严格保证操作顺序,避免旧数据覆盖新数据的问题。
3. 查询兜底补全方案
适合查询流量远大于写入流量、一致性要求不高的轻量化场景:
- 核心逻辑:所有写入ES的操作都使用业务唯一ID作为ES文档的
_id保证幂等性;用户查询ES时如果没有命中目标数据,自动回查Mongo是否存在该数据,如果存在则先把数据同步写入ES,再返回查询结果,用查询流量做兜底补偿。 - 优势:不需要额外开发后台任务,实现成本极低,适合小业务快速上线。
通用优化建议
- 所有ES写入操作都要做幂等校验,避免重试导致重复数据。
- 给ES写入配置内置的短周期重试机制,应对临时网络抖动,减少进入补偿逻辑的请求量。
- 如果涉及数据更新/删除操作,建议给所有操作加版本号标记,写入ES时只执行版本号更高的操作,避免乱序导致数据错误。
内容的提问来源于stack exchange,提问作者Jordi
相关产品推荐
相关产品推荐

