如何解决Elasticsearch频繁更新文档触发的version_conflict_engine_exception异常
异常原因
你遇到的报错是ES内置的乐观并发控制机制触发的:
Elasticsearch exception [type=version_conflict_engine_exception, reason=[111]: version conflict, required seqNo [4348], primary term [2]. current document has seqNo [4427] and primary term [2]]
ES为每个文档维护seq_no(序列号)和primary_term(主分区任期号)作为版本标识,默认的更新逻辑会校验请求携带的版本与文档当前版本是否一致,一致才会执行更新,避免并发更新导致脏写。
你设置1秒间隔仍然报错,说明1秒窗口内仍有多个针对同一文档的更新请求落到ES节点,导致版本校验失败。
解决方案
优先选择:开启ES内置冲突重试
ES更新API原生支持retry_on_conflict参数,指定后遇到版本冲突时ES会自动重试更新操作,无需业务侧额外加等待间隔,改造成本极低,可覆盖绝大多数高频更新场景。
以Java High Level REST Client为例,代码示例:
UpdateRequest updateRequest = new UpdateRequest("你的索引名", "111"); // 组装你的更新字段 updateRequest.doc(XContentFactory.jsonBuilder() .startObject() .field("待更新字段", "更新值") .endObject()); // 配置冲突时最多重试3次,可根据业务并发度调整为5-10次 updateRequest.retryOnConflict(3); client.update(updateRequest, RequestOptions.DEFAULT);
如果是用HTTP接口直接调用,只需要在请求URL上加retry_on_conflict=3参数即可:
POST /your_index/_update/111?retry_on_conflict=3 { "doc": { "待更新字段": "更新值" } }
高并发场景优化
如果配置重试后仍有冲突,说明单文档更新并发过高,可结合业务场景调整:
- 如果是数值累加类更新(比如统计点击量、累计金额),优先用painless脚本做服务端原子更新,不要在业务侧先查再改,脚本更新在ES侧单线程原子执行,可大幅降低冲突概率:
POST /your_index/_update/111?retry_on_conflict=3 { "script": { "source": "ctx._source.click_count += params.add_num", "params": { "add_num": 1 } } } - 若业务允许最终一致性,可在业务侧将同一文档的多个更新请求做合并批量执行,比如将1秒内同一个文档的多个更新请求合并为1个请求发送到ES,从根源降低冲突概率。
低一致性场景兜底方案(不推荐)
如果业务对数据更新一致性要求极低,可以不做版本校验直接覆盖文档,使用index接口直接写入最新的文档内容即可,不会触发版本冲突校验,但可能出现后发请求覆盖先发请求更新的脏写问题。
内容的提问来源于stack exchange,提问作者Manonandan S K

