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
}