Elasticsearch中的并发控制

Version字段

在访问document时,会返回一个version字段:

image-20220709171926806

如果后续对该document不断进行更新操作,version会递增,但Elasticsearch并不会存储历史的版本数据。当删除该文档时,会默认保留60s该版本号

以前Elasticsearch通过该version id实现乐观并发控制(optimistic concurrency control),但现在弃用了

另外,通过version的值,我们可以看出来该文档被修改过几次,但version的功能仅限于此了。

optimistic concurreny control

假设有这么一个场景,A和B同时拿到了商品的库存(in_stock = 6), A产生了购买操作,将in_stock更新为5

此时B也产生了购买操作,也将in_stock更新为5。这样计算肯定是有问题的

image-20220709094144031

所以要对数据操作实现并发控制,常见的方式有以下两种:

  • 悲观并发控制 假定冲突发生概率很高,在读取一行数据前,先锁定这一行,这样就可以确保只有读取到这行数据的线程可以修改这一行数据。
  • 乐观并发控制 这也是ES所使用的。假设冲突发生概率不高,也不会去阻止某一数据的访问。然而,如果基础数据在我们读取和写入的间隔中发生了变化,更新就会失败。这时候就由程序来决定如何处理这个冲突。例如,它可以重新读取新数据来进行更新,又或者它可以将这一情况直接反馈给用户。

以前ES使用version id来实现optimictic concurrency control,在更新数据时携带version id,例如针对上面的场景,A更新数据时携带version=1, 此时Elasticsearch验证没问题,更新成功后,version id变为2

B下次更新时,也携带了原来的version id=1,但此时数据版本为2,所以会更新失败:

image-20220709094234726

现在ES使用 _seq_no(sequence number) 和 _primary_term(primary term) 来确保文档的旧版本不会覆盖新的版本。sequence number在每次操作后递增,因此新的操作的_seq_no一定高于老的操作的_seq_no

image-20220709094329870

测试

获取Document的当前_seq_no:

image-20220709173500360

使用低版本的_seq_no尝试更新数据,发现会报错 version_conflict:

image-20220709173525804

使用相同的_seq_no来更新数据,成功更新并返回新的_seq_no

image-20220709173559658

如何处理失败

如果数据在我们读取和写入的间隔中发生了变化,更新就会失败。这时候就由程序来决定如何处理这个冲突。例如,它可以重新读取新数据(获取最新的_seq_no)来进行更新,又或者它可以将这一情况直接反馈给用户。