_seq_no和_primary_term
- _version表示当前数据新的版本号;
- _seq_no其实和version同一个道理,一旦数据发生更改,数据也一直是累计的;
- _primary_term表示是由谁分配的,意思说如果文档在一个集群里面,文档肯定会被分配一个位置,_primary_term表示的就是一个位置,文档所在主分片的编号;
- seq_no递增属于整个index,而不是单个文档
- if_seq_no 和 if_primary_term 是用来并发控制,他们和version不同,version属于单个文档,而seq_no属于整个index
- 删除文档,_seq_no还是会+1
对于if_seq_no和if_primary_term,官方文档已经有比较详细的叙述,https://www.elastic.co/guide/en/elasticsearch/reference/6.7/optimistic-concurrency-control.html
这里我说下简单的理解方式,对于if_primary_term记录的就是具体的哪个主分片,而if_seq_no这个参数起的作用和旧版本中的_version是一样的,之所以加上if_primary_term这个参数主要是提高并发的性能以及更自然,因为每个document都只会在某一个主分片中,所以由所在主分片分配序列号比由之前通过一个参数_version,相当于由整个ES集群分配版本号要来的更好。
简单翻译就是为确保较旧版本的文档不会覆盖较新版本,对文档执行的每个操作都会由协调该更改的主分片分配序列号。每次操作都会增加序列号,因此保证较新的操作具有比旧操作更高的序列号。然后,Elasticsearch可以使用序列号操作来确保更新的文档版本永远不会被分配给它的序列号更小的更改覆盖。
# 新增
PUT /test_index/_doc/4
{
"test_field": "test"
}
其中一个客户端,先更新了一下这个数据, 同时带上数据的版本号,确保说,es中的数据的版本 号,跟客户端中的数据的版本号(_seq_no)是相同的,才能修改
PUT /test_index/_doc/4
{
"test_field": "client1 changed"
}
# 返回
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "4",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1
}
另外一个客户端,尝试基于之前的数据去进行修改,同样带上(if_seq_no和 if_primary_term)version版本号,进行乐观锁的并发控制
PUT /test_index/_doc/4?if_seq_no=2&if_primary_term=1
{
"test_field": "client2 changed"
}
# 会报错
{
"error" : {
"root_cause" : [
{
"type" : "version_conflict_engine_exception",
"reason" : "[4]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]",
"index_uuid" : "Za52pNELQImcXB_OKIJM_A",
"shard" : "0",
"index" : "test_index"
}
],
"type" : "version_conflict_engine_exception",
"reason" : "[4]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]",
"index_uuid" : "Za52pNELQImcXB_OKIJM_A",
"shard" : "0",
"index" : "test_index"
},
"status" : 409
}
乐观锁就成功阻止并发问题 在乐观锁成功阻止并发问题之后,尝试正确的完成更新 重新进行GET请求,得到 version
基于最新的数据和版本号(以前是version 现在是if_seq_no和if_primary_term ),去进行修改,修 改后,带上最新的版本号,可能这个步骤会需要反复执行好几次,才能成功,特别是在多线程并发 更新同一条数据很频繁的情况下
version
内部版本控制和外部版本控制
- 内部版本控制:
_version
自增长,修改数据后,_version
会自动加1 - 外部版本控制: 为了保持
_version
与外部版本控制的数值一致,使用version_type=external
,检查数据当前的version值是否小于请求中的version值,一些关系型数据库使用时间戳当做版本控制,他把查询交给es,希望es也能使用这个版本控制
删除操作也会对这条数据的版本号加1
内部版本和外部版本在Elasticsearch内部是存储版本号是同一字段 _version,那么在对同一文档在通过不同版本类型覆盖文档时,_version是如何变化的呢
实质是按照上面说的一样,如果是内部版本覆盖,直接覆盖版本加1,如果是外部版本覆盖,会比较指定传入的版本号和文档当前版本号,大于则覆盖
查询返回版本号
GET /library/_search
{
"version": true,
"query": {
"term": {
"preview": "books"
}
}
}
内部版本控制
我们来保存个数据
PUT /library/book/1
{
"title": "es",
"price": "3"
}
保存完,结果显示
查询下
GET /library/book/1
更改当前版本,比如当前版本是3
POST /library/book/1/_update?version=3
{
"doc": {
"price": 15
}
}
更改完版本号加1
查询下
GET /library/book/1
外部版本控制
version_type=external,唯一的区别在于,_version,只有当你提供的version与es中的_version一模一样的时候,才可以进行修改,只要不一样,就报错;当version_type=external的时候,只有当你提供的version比es中的_version大的时候,才能完成修改
POST /library/book/1?version=100&version_type=external
{
"doc": {
"price": 20
}
}
意思就是请求,你带过去的version值要大于内部的version值就可以