ElasticSearch的并发控制问题

本文详细介绍了存储系统并发控制的基本思路,包括悲观锁和乐观锁。重点阐述了Elasticsearch采用的乐观锁策略,通过内部版本控制(_seq_no + _primary_term)和外部版本控制(version)确保数据一致性。通过示例展示了如何在更新操作中利用这些机制防止并发冲突,当版本不符时,更新操作会失败并返回错误信息。
摘要由CSDN通过智能技术生成

1.存储系统并发控制的基本解决思路

  1. 采用悲观锁

    这种方式,是说我确定一定会有较大或者非常大的可能产生并发,那么我就对数据资源加锁,防止并发操作时产生的数据冲突,从而对一类数据进行顺序执行。

  2. 采用乐观锁

    这是方式,是我假定并发不会发生或者很少发生,不会阻塞正在尝试的操作,如果数据在读写过程中被修改,更新就会失败,然后由应用程序来处理决定是否要进行冲突的解决以及解决的方式,例如重试、或者就默认更是失败。

2.ElasticSearch采用的并发控制

  1. ElasticSearch采用的是第二种,乐观锁并发控制

  2. ElasticSearch的乐观锁并发控制

    1. ElasticSearch的文档更新方式:我们知道,ElasticSearch的每个文档都是不可变的,如果要更新文档,其实是把源文档删除,同时增加一个全新的文档,并且把文档的version字段+1;
    2. 内部版本控制:采用_seq_no+_primary_term的方式进行并发控制;
    3. 使用外部版本控制:采用version+external的方式进行并发控制,注意这里是说不用es自己维护version,而是由外部程序来自己维护verison(这个version只能变大,不能变小哦);
  3. 正常的数据更新过程:应该是先查询出文档,然后再执行更新操作;

  4. ElasticSearch乐观锁并发控制的实例

    1. 先创建一个文档
    PUT accout/_doc/1
    {
      "user_id": 2,
      "account_amout": 30
    }
    

    结果:

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

    我们看到它返回了_seq_no和_primary_term。

    我们查询这个文档:

    GET accout/_doc/1
    

    返回结果:

    {
      "_index" : "accout",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 1,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user_id" : 2,
        "account_amout" : 30
      }
    }
    

    我们看到,同样也返回了_seq_no和_primary_term。

    2.内部版本控制的更新方式:

    PUT accout/_doc/1?if_primary_term=1&if_seq_no=1
    {
      "user_id":"2",
      "account_amout":31
    }
    

    返回:

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

    查询结果:

    GET accout/_doc/1
    
    {
      "_index" : "accout",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 2,
      "_seq_no" : 2,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user_id" : "2",
        "account_amout" : 31
      }
    }
    

    如果此时有另外的客户端也做更新,也拿到了 “_seq_no” : 2,"_primary_term" : 1,此时去更新:

    PUT accout/_doc/1?if_primary_term=1&if_seq_no=1
    {
      "user_id":"2",
      "account_amout":34
    }
    

    执行会报错:

    {
      "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" : "CkgluTniSO2lUONksRFJEA",
            "shard" : "0",
            "index" : "accout"
          }
        ],
        "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" : "CkgluTniSO2lUONksRFJEA",
        "shard" : "0",
        "index" : "accout"
      },
      "status" : 409
    }
    

    3.使用外部版本控制案例

    我们先查询一下文档:

    GET accout/_doc/1
    

    结果:

    {
      "_index" : "accout",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 2,
      "_seq_no" : 1,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user_id" : "2",
        "account_amout" : 34
      }
    }
    

    我们看到version=2,

    然后执行更新操作,将余额改为36,

    PUT accout/_doc/1?version=3&version_type=external
    {
      "user_id":"2",
      "account_amout":36
    }
    

    结果:

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

    如果此时有另外的客户端也做更新,也拿到了 _version:2,此时用version:3去更新:

    PUT accout/_doc/1?version=3&version_type=external
    {
      "user_id":"2",
      "account_amout":38
    }
    

结果:

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

出错 了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值