为什么Elasticsearch中bulk更新不会与update-by-query请求发生冲突?
问题解答
结论
完全存在update-by-query执行成功、bulk请求冲突失败的情况,你当前的测试场景没有触发,是因为参数和执行逻辑的特性导致冲突更易出现在update-by-query侧。
冲突原因修正
首先纠正你之前的推测:你看到的version conflict报错并不发生在主分片向副本同步的阶段,而是发生在主分片处理写请求的前置版本校验环节。seqNo(序列号)和primary term(主分片任期)的校验是主分片接收写请求后的第一环,校验通过才会执行写入、同步副本等后续操作,报错本质是请求携带的预期版本号已经落后于主分片上该文档的当前最新版本。
当前场景冲突集中在update-by-query侧的原因
两种请求的执行逻辑差异直接导致了你观测到的现象:
bulk更新是指定文档ID的直接更新:请求到达主分片后直接读取目标文档的当前版本做校验,校验通过立刻执行写入,整个过程链路短、耗时极短,版本校验和写入的间隔几乎可以忽略,很难被其他请求抢先修改版本。update-by-query是查询+更新的两步操作:它会先执行一次查询拿到匹配文档的快照(包含文档ID和当时的版本号),间隔一段时间后再逐个给这些文档发起更新请求。只要查询快照和执行更新的间隔中,bulk请求刚好修改了对应文档,update-by-query携带的旧版本号就会校验失败,触发冲突。
复现反向冲突的方法
你只要调整测试参数就可以观测到bulk侧冲突的情况:
- 取消
update-by-query的1秒请求间隔,同时给请求加上conflicts=proceed参数,提升update-by-query的执行频率 - 调低
bulk请求的发送频率,或者调大单次bulk请求包含的文档数量,拉长bulk的处理耗时
当update-by-query的更新操作刚好卡在bulk的版本校验和写入的间隙完成,就会导致bulk携带的预期版本号落后于最新版本,触发bulk侧的冲突报错。
可选优化方案
如果要降低冲突出现的概率,可以参考两个配置:
- 给
update-by-query请求添加retry_on_conflict=N参数,允许请求遇到版本冲突时自动重试N次 - 如果业务场景允许牺牲部分一致性换写入成功率,可以给
bulk更新设置version_type=external,不过该操作存在数据覆盖风险,需要谨慎评估
内容的提问来源于stack exchange,提问作者cosimoth
相关产品推荐
相关产品推荐

