行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。

并发修改文档导致版本冲突的问题

Elasticsearch | 作者 code4j | 发布于2018年03月10日 | 阅读数:6276

线上的场景可能会对一个文档同一秒进行并发修改,导致会出现个别的VersionConflictEngineException 异常,我猜测是并发upsert请求 可能存在先获取到版本号的请求 比 后获取到版本号的请求 执行慢或者执行晚导致的,毕竟默认es不会对文档操作加锁。但是如在不做锁机制的情况下处理这个问题呢。
已邀请:

code4j - coder github: https://github.com/rpgmakervx

赞同来自:

我今天看了下源码,发起upsert的时候,先prepare 一个 IndexRequest,这一步会发起一个GET 请求拿到这个文档,记录下version,然后发起一个TCP请求到UpdateAction。 在UpdateAction接收到的时候再取一次version,对比这两个是否相等。
 
所以问题来了:
case1:如果请求A B按顺序发起,A在prepare get 请求的时候,B也get到了,结果B执行的快,先一步对比完version然后版本+1,这个时候A再对比版本就出这个问题。这种情况下不影响数据一致性,毕竟B的结果是最新的,A失败了也没事儿。
case2:如果因为网络原因,A 发起 GET请求后B也拿到了version,但是下一步A执行的快,先一步对比完version然后+1了,这个时候B就失败了,这种情况下文档就不是最新的了。
 
那么知道这个原理了,可是仍旧不知道要怎么处理这种问题好?难不成我人为去给每个文档加锁吗?加锁竞争激烈的时候势必性能受影响。不知道大家工作中遇到这种问题是怎么处理的呢?

strglee

赞同来自:

es版本控制有内部和外部两种类型。默认情况下,es使用内部版本控制。
version_type=external的时候是外部值控制。 在使用外部版本类型时,系统会检查传递给索引请求的版本号是否大于当前存储的文档的版本,如果为true,则文档将被索引并使用新的版本号。如果提供的值小于或等于存储文档的版本号,则会发生版本冲突,索引操作将失败。
 
所以最简单的实现方式就是每次更新使用当前时间戳作为版本号,
 
PUT /test/test/2?version=1520834740000&version_type=external

时间戳精确到了毫秒
https://www.elastic.co/guide/e ... types  

cyberdak

赞同来自:

外部确保网络上的顺序执行只能最简单的就是串行化了,但是不知道带来的效率降低你是否能接受

gtexpanse

赞同来自:

如果es的乐观锁没法解决你的场景的话,我觉得你应该在操作同一文档的时候就自行做好“排他锁”一样的机制了。比如kafka的相同数据路由到相同分片,jstorm的group机制等等,都能解决同一文档并发的问题。

要回复问题请先登录注册