• ES之4:Elasticsearch并发控制及乐观锁实现原理


    普通关系型数据库使用的是(悲观并发控制(PCC))

    当我们在修改一个数据前先锁定这一行,然后确保只有读取到数据的这个线程可以修改这一行数据

    ES使用的是(乐观并发控制(OCC))  

      ES不会阻止某一数据的访问,然而,如果基础数据在我们读取和写入的间隔中发生了变化,更新就会失败,这时候就由程序来决定如何处理这个冲突。它可以重新读取新数据来进行更新,又或者将这一情况直接反馈给用户。

    本文中的示例是在ES7.1.1上执行的。

    ES如何实现版本控制(使用es内部版本号)

    1)首先得到想要修改的文档,获取版本version号

    先为duan的索引增加一条数据,我执行了2次:

    PUT /duan/_doc/1
    {
      "member_id":"abc",
      "amount":302,
      "event_time":"2020-05-03 10:07:01"
    }

    返回:

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

    或者直接查询一下:

    GET /duan/_doc/1

    {
      "_index" : "duan",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 2,
      "_seq_no" : 1,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "member_id" : "abc",
        "amount" : 302,
        "event_time" : "2020-05-03 10:07:01"
      }
    }

    返回中的_version就是ES为我们准备的内部version了。

    使用内置_version实战乐观锁控制效果

    当前的version是2,我们使用一个浏览器标签页,发出更新请求,把当前的version带上:

    POST /duan/_doc/1?version=2
    {
    "member_id":"abc",
    "amount":303,
    "event_time":"2020-05-03 10:07:01"
    }

    返回:

    {
      "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;"
          }
        ],
        "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;"
      },
      "status": 400
    }

    百度了下,是ES版本支持的问题

    这时我们在操作es的时候可以通过_seq_no这个es自带的字段进行控制
    注意:一些老的版本es使用version,但是新版本不支持了,会报这个错误,提示我们用if_seq_no和if_primary_term

    版本元数据
    _seq_no:文档版本号,作用同_version(相当于学生编号,每个班级的班主任为学生分配编号,效率要比学校教务处分配来的更加高效,管理起来更方便)
    _primary_term:文档所在位置(相当于班级)
    官文地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/optimistic-concurrency-control.html

    正确的做法如下:

    POST /duan/_doc/1/_update?if_seq_no=2&if_primary_term=1
    {
      "doc":{
       "amount":333
      }
    }

    返回:

    #! Deprecation: [types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.
    {
    "_index" : "duan",
    "_type" : "mytype",
    "_id" : "1",
    "_version" : 3,
    "result" : "updated",
    "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
    },
    "_seq_no" : 2,
    "_primary_term" : 1
    }

    相同的if_seq_no=2&if_primary_term=1再执行一次,失败的结果如下:

    {
      "error": {
        "root_cause": [
          {
            "type": "version_conflict_engine_exception",
            "reason": "[1]: version conflict, required seqNo [1], primary term [1]. current document has seqNo [2] and primary term [1]",
            "index_uuid": "TsS_bkFJRE6wdpOSzIrcEw",
            "shard": "0",
            "index": "duan"
          }
        ],
        "type": "version_conflict_engine_exception",
        "reason": "[1]: version conflict, required seqNo [1], primary term [1]. current document has seqNo [2] and primary term [1]",
        "index_uuid": "TsS_bkFJRE6wdpOSzIrcEw",
        "shard": "0",
        "index": "duan"
      },
      "status": 409
    }

    关键错误信息:version_conflict_engine_exception,版本冲突,将if_seq_no升到3,模拟失败后重试,此时更新成功。

    真实的场景,重试的次数跟线程并发数有关,线程越多,更新越频繁,就可能需要重试多次才可能更新成功。

    使用外部_version实战乐观锁控制效果

    ES允许不使用内置的version进行版本控制,可以自定义使用外部的version,例如常见的使用Elasticsearch做数据查询加速的经典方案,关系型数据库作为主数据库,然后使用Elasticsearch做搜索数据,主数据会同步数据到Elasticsearch中,而主数据库并发控制,本身就是使用的乐观锁机制,有自己的一套version生成机制,数据同步到ES那里时,直接使用更方便。

    请求语法上加上version_type参数即可:

    POST /duan/_doc/1?version=2&version_type=external
    {
      "doc":{
       "amount":999
      }
    }

    返回:

    {
      "error": {
        "root_cause": [
          {
            "type": "version_conflict_engine_exception",
            "reason": "[1]: version conflict, current version [3] is higher or equal to the one provided [2]",
            "index_uuid": "TsS_bkFJRE6wdpOSzIrcEw",
            "shard": "0",
            "index": "duan"
          }
        ],
        "type": "version_conflict_engine_exception",
        "reason": "[1]: version conflict, current version [3] is higher or equal to the one provided [2]",
        "index_uuid": "TsS_bkFJRE6wdpOSzIrcEw",
        "shard": "0",
        "index": "duan"
      },
      "status": 409
    }

    调整下version=3,再执行一次

    {
      "_index" : "duan",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 4,
      "result" : "updated",
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 3,
      "_primary_term" : 1
    }
    唯一的区别
    • 内置_version,只有当你提供的version与es中的_version完全一样的时候,才可以进行更新,否则报错;
    • 外部_version,只有当你提供的version比es中的_version大的时候,才能完成修改。

    Replica Shard数据同步并发控制

    在Elasticsearch内部,每当primary shard收到新的数据时,都需要向replica shard进行数据同步,这种同步请求特别多,并且是异步的。如果同一个document进行了多次修改,Shard同步的请求是无序的,可能会出现"后发先至"的情况,如果没有任何的并发控制机制,那结果将无法相像。

    Shard的数据同步也是基于内置的_version进行乐观锁并发控制的。

    例如Java客户端向Elasticsearch某条document发起更新请求,共发出3次,Java端有严谨的并发请求控制,在ElasticSearch的primary shard中写入的结果是正确的,但Elasticsearch内部数据启动同步时,顺序不能保证都是先到先得,情况可能是这样,第三次更新请求比第二次更新请求先到,如下图:

    如果Elasticsearch内部没有并发的控制,这个document在replica的结果可能是text2,并且与primary shard的值不一致,这样肯定错了。

    预期的更新顺序应该是text1-->text2-->text3,最终的正确结果是text3。那Elasticsearch内部是如何做的呢?

    Elasticsearch内部在更新document时,会比较一下version,如果请求的version与document的version相等,就做更新,如果document的version已经大于请求的version,说明此数据已经被后到的线程更新过了,此时会丢弃当前的请求,最终的结果为text3。
    此时的更新顺序为text1-->text3,最终结果也是对的。


    原文链接:https://blog.csdn.net/kuangxie4668/java/article/details/105547435

    参考:https://www.cnblogs.com/huangying2124/archive/2019/12/05/11986897.html

  • 相关阅读:
    oracle语法
    cin.clear()、cin.sync()
    Intellij Idea Spring Boot 热部署
    navicat_premium_x64最新版安装说明
    激活navicat premium12
    时间戳Unix和时间之间的转换
    排序算法-- 大总结
    The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    final
    static
  • 原文地址:https://www.cnblogs.com/duanxz/p/5209058.html
Copyright © 2020-2023  润新知