Elasticsearch 为什么会产生文档版本冲突?如何避免?

这篇具有很好参考价值的文章主要介绍了Elasticsearch 为什么会产生文档版本冲突?如何避免?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、Elasticsearch 版本冲突复现

先让大家直观的看到 Elasticsearch 文档版本冲突。

1.1 场景1:create 场景

DELETE my-index-000001
# 执行创建并写入
PUT my-index-000001/_create/1
{
  "@timestamp": "2099-11-15T13:12:00",
  "message": "GET /search HTTP/1.1 200 1070000",
  "user": {
    "id": "kimchy"
  }
}

# 再次执行会报版本冲突错误。
# 报错信息:[1]: version conflict, document already exists (current version [1])
PUT my-index-000001/_create/1
{
  "@timestamp": "2099-11-15T13:12:00",
  "message": "GET /search HTTP/1.1 200 1070000",
  "user": {
    "id": "kimchy"
  }
}

Elasticsearch 为什么会产生文档版本冲突?如何避免?

1.2 场景2:批量更新场景模拟

模拟脚本1:循环写入数据 index.sh。

Elasticsearch 为什么会产生文档版本冲突?如何避免?

模拟脚本2:循环update_by_query 批量更新数据 update.sh。

Elasticsearch 为什么会产生文档版本冲突?如何避免?

由于:写入脚本 index.sh 比更新脚本 update.sh (执行一次,休眠1秒)执行要快,所以更新获取的版本较写入的最新版本要低,会导致版本冲突如下图所示:

Elasticsearch 为什么会产生文档版本冲突?如何避免?

Elasticsearch 为什么会产生文档版本冲突?如何避免?

1.3 场景3:批量删除场景模拟

写入脚本 index.sh 不变。

删除脚本 delete.sh 如下:

Elasticsearch 为什么会产生文档版本冲突?如何避免?

和更新原因一致,由于:写入脚本 index.sh 比删除脚本 delete.sh (执行一次,休眠1秒)执行要快,所以删除获取的版本较写入的最新版本要低,会导致版本冲突如下图所示:

Elasticsearch 为什么会产生文档版本冲突?如何避免?

2、Elasticsearch 文档版本定义

执行:

GET test/_doc/1

召回结果如下:

Elasticsearch 为什么会产生文档版本冲突?如何避免?

这里的 version 代表文档的版本。

  • 当我们在 Elasticsearch 中创建一个新文档时,它会为该文档分配一个_version: 1。

  • 当我们对该文档进行任何后续更新(更新 update、索引 index 或删除 delete)时,_version都会增加 1。

一句话:Elasticsearch 使用_version来鉴别文档是否已更改。

3、Elasticsearch 文档版本产生背景

试想一下,如果没有文档版本?当有并发访问会怎么办?

前置条件:Elasticsearch 从写入到被检索的时间间隔是由刷新频率 refresh_interval 设定的,该值可以更新,但默认最快是 1 秒。

Elasticsearch 为什么会产生文档版本冲突?如何避免?

如上图所示,假设我们有一个人们用来评价 T 恤设计的网站。网站很简单,仅列出了T恤设计,允许用户给T恤投票。如果顺序投票,没有并发请求,直接发起update更新没有问题。

但是,在999累计投票数后,碰巧小明同学和小红同学两位同时(并发)发起投票请求,这时候,如果没有版本控制,将导致最终结果不是预期的1001,而是1000。

所以,为了处理上述场景以及比上述更复杂的并发场景,Elasticsearch 亟需一个内置的文档版本控制系统。这就是 _version 的产生背景。

https://kb.objectrocket.com/elasticsearch/elasticsearch-version-history-what-it-does-and-doesnt-do-501

https://www.elastic.co/cn/blog/elasticsearch-versioning-support

4、常见的并发控制策略

并发控制可以简记为:“防止两个或多个用户同时编辑同一记录而导致最终结果和预期不一致”。

常见的并发控制策略:悲观锁、乐观锁。

4.1 悲观锁

悲观锁,又名:悲观并发控制,英文全称:"Pessimistic Concurrency Control",缩写“PCC”,是一种并发控制的方法。

  • 悲观锁本质:在修改数据之前先锁定,再修改。

  • 悲观锁优点:采用先锁定后修改的保守策略,为数据处理的安全提供了保证。

  • 悲观锁缺点:加锁会有额外的开销,还会增加产生死锁的机会。

  • 悲观锁应用场景:比较适合写入操作比较频繁的场景。

4.2 乐观锁

乐观锁,又名:乐观并发控制,英文全称:“Optimistic Concurrency Control”,缩写OCC”,也是一种并发控制的方法。

  • 乐观锁本质:假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

  • 乐观锁优点:“胆子足够大,足够乐观”,直到提交的时候才去锁定,不会产生任何锁和死锁。

  • 乐观锁缺点:并发写入会有问题,需要有冲突避免策略补救。

  • 乐观锁应用场景:数据竞争(data race)不大、冲突较少的场景、比较适合读取操作比较频繁的场景,确保比其他并发控制方法(如悲观锁)更高的吞吐量。

这里要强调的是,Elasticsearch 采用的乐观锁的机制来处理并发问题。

Elasticsearch 乐观锁本质是:没有给数据加锁,而是基于 version 文档版本实现。每次更新或删除数据的时候,都需要对比版本号。

5、Elasticsearch 文档版本冲突的本质

一句话,Elasticsearch 文档冲突的本质——老版本覆盖掉了新版本。

6、如何解决或者避免 Elasticsearch 文档版本冲突?

6.1 external 外部控制版本号

“external”——我的理解就是“简政放权”,交由外部的数据库或者更确切的说,是写入的数据库或其他第三方库来做控制。

版本号可以设置为外部值(例如,如果在数据库中维护)。要启用此功能,version_type应设置为 external。

使用外部版本类型 external 时,系统会检查传递给索引请求的版本号是否大于当前存储文档的版本。

  • 如果为真,也就是新版本大于已有版本,则文档将被索引并使用新的版本号。

  • 如果提供的值小于或等于存储文档的版本号,则会发生版本冲突,索引操作将失败。

好处:不论何时,ES 中只有最新版本的数据,借助 external 相对有效的解决版本冲突问题。

实战一把:

如果没有 external,执行如下命令:

PUT my-index-000001/_doc/1?version=2
{
  "user": {
    "id": "elkbee"
  }
}

报错如下:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "action_request_validation_exception",
        "reason" : "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;"
      }
    ],
.......省略2行......
  "status" : 400
}

啥意思呢?内部版本控制(internal)不能用于乐观锁,也就是直接使用 version 是不可以的。需要使用:if_seq_noif_primary_term,它俩的用法,后文会有专门解读。

如果用 external,执行如下命令:

PUT my-index-000001/_doc/1?version=2&version_type=external
{
  "user": {
    "id": "elkbee"
  }
}

执行结果如下:

{
  "_index" : "my-index-000001",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

相比于之间没有加 external,加上 external 后,可以实现基于version的文档更新操作。

external_gt 和 external_gte的用法见官方文档,本文不展开,原理同 external。

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/docs-index_.html#index-versioning

6.2 通过if_seq_no 和 if_primary_term 唯一标识避免冲突

索引操作(Index,动词)是有条件的,并且只有在对文档的最后修改分配了由 if_seq_no 和 if_primary_term 参数指定的序列号和 primary term specified(翻译起来拗口,索性用英文)才执行。

如果检测到不匹配,该操作将产生一个 VersionConflictException 409 的状态码。

Step1:写入数据
DELETE products_001
PUT products_001/_doc/1567
{
  "product" : "r2d2",
  "details" : "A resourceful astromech droid"
}


# 查看ifseqno 和 ifprimaryterm 
GET products_001/_doc/1567

返回:

{
  "_index" : "products_001",
  "_type" : "_doc",
  "_id" : "1567",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "product" : "r2d2",
    "details" : "A resourceful astromech droid"
  }
}
Step2:以这种方式更新,前提是先拿到 if_seq_no 和 if_primary_term
# 模拟数据打tag 过程
PUT products_001/_doc/1567?if_seq_no=0&if_primary_term=1
{
  "product": "r2d2",
  "details": "A resourceful astromech droid",
  "tags": [ "droid" ]
}


# 再获取数据
GET products_001/_doc/1567

返回:

{
  "_index" : "products_001",
  "_type" : "_doc",
  "_id" : "1567",
  "_version" : 2,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "product" : "r2d2",
    "details" : "A resourceful astromech droid",
    "tags" : [
      "droid"
    ]
  }
}

step2 更新数据的时候,是在 step1 的获取已写入文档的 if_seq_no=0 和 if_primary_term=1 基础上完成的。

这样能有效避免冲突。

6.3 批量更新和批量删除忽略冲突实现

如下是在开篇的基础上加了:conflicts=proceed。

conflicts 默认值是终止,而 proceed 代表继续。

POST test/_update_by_query?conflicts=proceed
{
  "query": {
    "match": {
      "name": "update"
    }
  },
  "script": {
    "source": "ctx._source['foo'] = '123ss'",
    "lang": "painless"
  }
}

conflicts=proceed 的本质——告诉进程忽略冲突并继续更新其他文档。

开篇不会报 409 错误了,但依然会有版本冲突。但,某些企业级场景是可以用的。

Elasticsearch 为什么会产生文档版本冲突?如何避免?

同理,delete_by_query 参数及返回结果均和 update_by_query 一致。

Elasticsearch 为什么会产生文档版本冲突?如何避免?

扩展:单个更新 update (区别于批量更新:update_by_query)有 retry_on_conflict 参数,可以设置冲突后重试次数。

7、关于频繁更新带来的性能问题

正如文章开篇演示的,并发更新或者并发删除可能会导致版本冲突。

除了并发性和正确性之外,请注意,非常频繁地更新文档可能会导致性能下降。

如果更新了尚未写入段(segment)的文档,将会导致刷新操作。而刷新频率越小(企业级咨询我见过设置小于1s的,不推荐),势必会导致写入低效。

更多探讨推荐阅读:

https://discuss.elastic.co/t/handling-conflicts/135240/2

8、小结

从实际问题抽象出模拟脚本,让大家看到文档版本冲突是如何产生的。而后,定义了版本冲突并指出了其产生的背景。

接着,详细讲解了解决冲突的两种机制:乐观锁、悲观锁。探讨、验证了解决文档版本冲突的几种方案。

你有没有遇到过本文提及的问题,如何解决的呢?欢迎留言交流。

参考

[1] https://www.anycodings.com/1questions/160352/why-bulk-update-never-conflicts-with-update-by-query-requests-in-elasticsearch

[2] https://learnku.com/articles/43867

[3] https://www.elastic.co/guide/en/elasticsearch/reference/current/optimistic-concurrency-control.html

[4] https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#optimistic-concurrency-control-index

[5] https://www.elastic.co/guide/en/elasticsearch/reference/8.1/docs-index_.html#index-versioning

推荐阅读

  1. 如何从0到1打磨一门 Elasticsearch 线上直播课?

  2. 重磅 | 死磕 Elasticsearch 方法论认知清单(2021年国庆更新版)

  3. 如何系统的学习 Elasticsearch ?                      

Elasticsearch 为什么会产生文档版本冲突?如何避免?

比同事抢先一步学习进阶干货!文章来源地址https://www.toymoban.com/news/detail-404796.html

到了这里,关于Elasticsearch 为什么会产生文档版本冲突?如何避免?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Elasticsearch为什么快?

    概括的说,一个优秀的搜索引擎的设计,至少应该具备以下几点要求: 高效的压缩算法 快速的编码和解码算法 合理的数据结构 通用最小化算法 结合以上几点,后面我将通过一个案例来讲解,倒排索引的基本原理是什么。在了解“倒排索引”之前,我们先来看一下何为“索

    2024年02月04日
    浏览(39)
  • 【word密码】Word 文档设置了只读为什么还能编辑?

    有些朋友可能会遇到这种疑问,为什么我的Word文档设置了只读模式,还是可以编辑的,这是什么原因呢? 其实是因为大部分的只读模式,设置完成之后都是可以编辑的,但是当我们进行保存的时候就会发现,word提示需要重命名并选择新路径才能够保存。 这种操作,即使可以

    2024年01月25日
    浏览(62)
  • Elasticsearch:索引状态是红色还是黄色?为什么?

    在我之前文章 “Elasticsearch:如何调试集群状态 - 定位错误信息” 中,我有详细介绍如何调试集群状态。在今天的文章中,我将详细介绍如何故障排除和修复索引状态。 Elasticsearch 是一个伟大而强大的系统,特别是创建一个可扩展性极强的分布式数据存储,并自动跟踪、管理

    2023年04月09日
    浏览(41)
  • Elasticsearch:什么是向量和向量存储数据库,我们为什么关心?

    Elasticsearch 从 7.3 版本开始支持向量搜索。从 8.0 开始支持带有 HNSW 的 ANN 向量搜索。目前 Elasticsearch 已经是全球下载量最多的向量数据库。它允许使用密集向量和向量比较来搜索文档。 向量搜索在人工智能和机器学习领域有许多重要的应用。 有效存储和检索向量的数据库对于

    2024年02月08日
    浏览(51)
  • ElasticSearch(七):ES查询速度为什么那么快

    介绍给大家一个开源SpringCloud项目。整合了大部分开源中间件,详情信息可以查看文档: spring cloud开源组件开发 另外自己以后博客所讲解的代码内容,都会我的Git上同步(GitHub同步)GIT地址 ES使用的数据结构是倒排索引,在对搜索内容进行分词的时候,会根据搜索内容分词结

    2023年04月08日
    浏览(78)
  • 为什么选择elasticsearch分布式搜索引擎

    elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 例如: 在CSDN上搜索代码 在电商网站搜索商品 在百度搜索答案 elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监

    2024年02月12日
    浏览(49)
  • 入门ElasticSearch :为什么选择ES作为搜索引擎?

    随着数据量的不断增长,搜索和分析大规模数据集变得越来越重要。传统数据库在面对这种需求时往往表现不佳,这时候就需要一种专门用于搜索和分析的引擎。ElasticSearch (简称ES)就是这样一款强大的搜索引擎,它具有许多优势,使得它成为许多企业和开发者的首选。 简

    2024年02月09日
    浏览(46)
  • ElasticSearch第七讲 ES查询速度为什么那么快

    介绍给大家一个开源SpringCloud项目。整合了大部分开源中间件,详情信息可以查看文档: spring cloud开源组件开发 另外自己以后博客所讲解的代码内容,都会我的Git上同步(GitHub同步)GIT地址 ES使用的数据结构是倒排索引,在对搜索内容进行分词的时候,会根据搜索内容分词结

    2023年04月25日
    浏览(54)
  • ElasticSearch第七讲:ES查询速度为什么那么快

    介绍给大家一个开源SpringCloud项目。整合了大部分开源中间件,详情信息可以查看文档: spring cloud开源组件开发 另外自己以后博客所讲解的代码内容,都会我的Git上同步(GitHub同步)GIT地址 ES使用的数据结构是倒排索引,在对搜索内容进行分词的时候,会根据搜索内容分词结

    2023年04月19日
    浏览(48)
  • SpringBoot有的时候引入依赖为什么不用加版本号

    有的小伙伴做项目时候,引入新的包时候,会有疑问,为什么有些依赖需要加版本号,有些依赖不需要加版本号?不加版本号的依赖,版本号都写在哪里了呢? 内置的依赖可以不加版本号 这是因为SpringBoot内置了很多依赖,引入这些内置的依赖时不需要加版本号,相反,如果

    2024年01月19日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包