【README】
- 1.本文介绍了es更新文档时的并发控制策略;
- 2.通过版本号实现并发控制(类似于mysql中基于版本号的乐观锁);
- 3.Es为支持并发控制,为每篇文章设置了版本号_version。初始值为1,每更新1次加1。
- 4.文档初始数据如下:
get localhost:9200/myshare/_doc/12{"_index": "myshare","_type": "_doc","_id": "12","_version": 5,"_seq_no": 20,"_primary_term": 2,"found": true,"_source": {"addr": "sichuan chengdu12 after update by post with _update2","age": "12","email": "12@gmai.com","name": "zhang san12 after update by post with _update2"}
}
【1】通过version版本号更新文档(报错)
post localhost:9200/myshare/_doc/12?version=5
{"doc":{"addr":"sichuan chengdu12 after update by version=5 , "name":"zhang san12 after update by version=5"}
} // 结果:抛出异常
{"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;"}] },"status": 400
}
【异常报文解说】
- "reason": "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;" (内部版本号不能用于并发控制,请使用 _seq_no 和 primary_term)
【2】通过 seq_no 和 primary_term作为版本号更新文档 (成功)
post localhost:9200/myshare/_doc/12?if_seq_no=20&if_primary_term=2
{"doc":{"addr":"sichuan chengdu12 after update by if_seq_no=20&if_primary_term=2" , "name":"zhang san12 after update by if_seq_no=20&if_primary_term=2"}
}
// 响应报文
{"_index": "myshare","_type": "_doc","_id": "12","_version": 6,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 22,"_primary_term": 2
}
【解说】
- 显然,更新成功了;
- 版本号 _version=6, _seq_no =22 加1, _primary_term 没变 ;
【3】通过外部版本号更新文档
【3.1】根据 version=6&version_type=external 外部版本号更新文档(报错)
post localhost:9200/myshare/_doc/12?version=6&version_type=external
{"doc":{"addr":"sichuan chengdu12 after update by version_type=external" , "name":"zhang san12 after update by version_type=external"}
}// 响应报文
{"error": {"root_cause": [{"type": "version_conflict_engine_exception","reason": "[12]: version conflict, current version [6] is higher or equal to the one provided [6]","index_uuid": "FpuW3cmhR9Ws3M5JRzRgZA","shard": "0","index": "myshare"}],…},"status": 409
}
【解说】报错原因:
- "reason": "[12]: version conflict, current version [6] is higher or equal to the one provided [6]",
- 版本号冲突,当前版本号6大于等于给定的版本号6。
- 即文档当前版本号应该小于 给定版本号。
- 小结:在使用外部版本做文档更新并发控制时,传入的版本号需要大于文档本身版本号。
-
【3.2】使用外部版本号且版本号大于文档本身版本号更新文档(成功)
-
post localhost:9200/myshare/_doc/12?version=7&version_type=external {"doc":{"addr":"sichuan chengdu12 after update by version_type=external" , "name":"zhang san12 after update by version_type=external"} }// 响应报文 {"_index": "myshare","_type": "_doc","_id": "12","_version": 7,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 23,"_primary_term": 2 }
【4】 补充
-
【4.1】primary term (主项)
- es集群每次故障转移期间不同的分片成为主分片时,primary term 都会增加。
- 这有助于解决重新联机的旧主节点上发生的更改与新主节点上发生的更改。
refer2 What is seq_no and primary_term in elasticsearch? - DevOpsSchool.com
每次故障转移期间不同的分片成为主分片时,主项都会增加。这有助于解决重新联机的旧主节点上发生的更改与新主节点上发生的更改(新的胜利)。
复制组的主要术语只是主分片更改次数的计数器。
这些主要术语是增量的,并且在提升主要术语时会发生变化。它们保持在集群状态中,因此代表了集群所在的一种“版本”或“一代”的主节点。
为了确保文档的旧版本不会覆盖新版本,对文档执行的每个操作都由协调该更改的主分片分配一个序列号。
假设您的索引由 5 个主分片组成(这是版本 7 之前的默认值)。索引和更新请求是针对主分片执行的。如果您有多个主分片,elasticsearch 能够将传入请求(例如巨大的批量请求)并行化/分发到多个分片以提高性能。
因此,primary_term 提供了有关执行/协调更改/更新的主分片(在此示例中为#1、#2、#3、#4 或#5)的信息。
-
【4.2】_version 与 _seq_no 的区别
- 1)_version :是一个序号,用于统计文档被更新(或创建)的次数;
- 2)_seq_no:是一个序号,用于统计索引被操作的次数;
-
【例子】 _version 与 _seq_no 区分
- 1)索引文档20221106
-
put localhost:9200/myshare/_doc/20221106 {"addr":"sichuan chengdu-20221106", "age":"20221106", "email":"20221106@gmai.com", "name":"zhang san-20221106" }// 响应报文 {"_index": "myshare","_type": "_doc","_id": "20221106","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 25,"_primary_term": 3 }
【解析】
-
_version 等于1;
-
_seq_no 等于 25;
-
2)索引文档 20221107
-
put localhost:9200/myshare/_doc/20221107 {"addr":"sichuan chengdu-20221107", "age":"20221107", "email":"20221107@gmai.com", "name":"zhang san-20221107" }// 响应报文 {"_index": "myshare","_type": "_doc","_id": "20221107","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 26,"_primary_term": 3 }
【解析】
-
-
_version 等于1;
-
_seq_no 等于 26;
-
-
我想应该可以理解 seq_no 与 version的区别了;