ElasticSearch--基于_version进行乐观锁并发控制

PUT accounts/person/6
{
  "user": "lisi",
  "age":50,
  "salary":6000,
  "title": "业务员",
  "desc": "数据库管理"
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "6",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

1 当第一次创建一个document的时候,其_version为1,之后对document进行修改和删除其内部都会执行+1的操作

2 当出现并发更新的时候,ES内部会基于_version进行乐观锁并发控制(乐观锁和悲观锁机制,可以参考数据库的相关机制)

  • version机制:更新的时候带上version参数,如果version和es的_version一致时才更新,更新后_version自增,从而保证多条线程同时更改只有一个线程可以修改成功。先执行成功的为主,后来的不合法被舍弃(抛出异常)
  • retry_on_conflict机制:partial update更新的时候带上retry_on_conflict参数,这个参数规定了失败之前 update 应该重试的次数,它的默认值为 0 。当重试的时候,会先对比es中的数据和提交的数据是否一样,如果一样,_version者不进行自增操作。如此便可保证最后提交的线程得到修改,也可以在一定程度上减少partial update更新时的线程冲突。对于部分更新的很多使用场景,文档已经被改变也没有关系。 例如,如果两个进程都对页面访问量计数器进行递增操作,它们发生的先后顺序其实不太重要; 如果冲突发生了,我们唯一需要做的就是尝试再次更新。

  • 带上版本号进行更新

说明:当提供的version版本号和es中的_version相等的时候,才能成功修改

1 增加一条数据

PUT accounts/person/9
{
  "user": "lisi",
  "age":50,
  "salary":6000,
  "title": "业务员",
  "desc": "数据库管理"
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "9",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

2 更新,带上version=1参数

POST /accounts/person/7/_update?version=1
{
  "doc": {
    "age": 100
  }
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "7",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

3 再次更新,还是带上version=1参数

POST /accounts/person/7/_update?version=1
{
  "doc": {
    "age": 100
  }
}
返回:
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[person][7]: version conflict, current version [2] is different than the one provided [1]",
        "index_uuid": "qgPjJUv6Sm-VBesoOMlNPA",
        "shard": "3",
        "index": "accounts"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[person][7]: version conflict, current version [2] is different than the one provided [1]",
    "index_uuid": "qgPjJUv6Sm-VBesoOMlNPA",
    "shard": "3",
    "index": "accounts"
  },
  "status": 409
}

说明:因为第一次更新的时候,_version会变为2,所以此次更新不会成功


  • 使用version_type=external进行并发控制

说明:当加上version_type=external的时候,只有当你提供的version大于es中的_version的时候,才能成功修改

1 增加一条数据(增加时指定version为1)

PUT accounts/person/11?version=1&version_type=external
{
  "age": 100
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "11",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

2 替换更新,带上version=1&version_type=external参数,更新失败

PUT accounts/person/11?version=1&version_type=external
{
  "age": 100
}
返回:
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[person][11]: version conflict, current version [1] is higher or equal to the one provided [1]",
        "index_uuid": "qgPjJUv6Sm-VBesoOMlNPA",
        "shard": "4",
        "index": "accounts"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[person][11]: version conflict, current version [1] is higher or equal to the one provided [1]",
    "index_uuid": "qgPjJUv6Sm-VBesoOMlNPA",
    "shard": "4",
    "index": "accounts"
  },
  "status": 409
}

3 再次替换更新,带上version=2&version_type=external参数,更新成功,此时的版本号为_version=2

PUT accounts/person/11?version=2&version_type=external
{
  "age": 100
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "11",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": false
}

注意:只有替换方式才支持version_type=external参数,partial update更新不支持,如下:

POST /accounts/person/10/_update?version=1&version_type=external
{
  "doc": {
    "age": 100
  }
}

会返回错误:

{
  "error": {
    "root_cause": [
      {
        "type": "action_request_validation_exception",
        "reason": "Validation Failed: 1: version type [EXTERNAL] is not supported by the update API;"
      }
    ],
    "type": "action_request_validation_exception",
    "reason": "Validation Failed: 1: version type [EXTERNAL] is not supported by the update API;"
  },
  "status": 400
}

  • 带上retry_on_conflict参数进行更新

1 PUT accounts/person/19
{
  "age": 100
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "19",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

2 POST /accounts/person/19/_update?retry_on_conflict=5
{
  "doc": {
    "age":10
  }
}
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "19",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

3 POST /accounts/person/19/_update?retry_on_conflict=5
{
  "doc": {
    "age":10
  }
}
返回:result为noop,者没有发生任何操作
{
  "_index": "accounts",
  "_type": "person",
  "_id": "19",
  "_version": 2,
  "result": "noop",
  "_shards": {
    "total": 0,
    "successful": 0,
    "failed": 0
  }
}

4 再次查看,版本号未改变:
GET accounts/person/19
返回:
{
  "_index": "accounts",
  "_type": "person",
  "_id": "19",
  "_version": 2,
  "found": true,
  "_source": {
    "age": 10
  }
}

注意:version和retry_on_conflict参数是互斥的,如下:

POST /accounts/person/19/_update?version=2&retry_on_conflict=5
{
  "doc": {
    "age":10
  }
}

会返回错误:

{
  "error": {
    "root_cause": [
      {
        "type": "action_request_validation_exception",
        "reason": "Validation Failed: 1: can't provide both retry_on_conflict and a specific version;"
      }
    ],
    "type": "action_request_validation_exception",
    "reason": "Validation Failed: 1: can't provide both retry_on_conflict and a specific version;"
  },
  "status": 400
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值