ES 7.8.0(三) 文档冲突
文档冲突就是你在访问一个文档的时候,别人也在访问这个文档,正常来说文档的最终版本,应该是最后一个修改的人,而且这种方式只适合用于全量修改,但是如果我是局部修改,就会造成把前面修改文档的内容也覆盖掉,这就是文档冲突。
解决这种文档冲突的方式,在数据库领域中有两种方式用来确保并发更新时变更不会丢失:
- 悲观锁
这种方式被关系型数据库广泛使用,他假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。一个典型的例子是读取一行数据之前先将其锁住,确保只有方式锁的线程能够对这行数据进行修改。 - 乐观锁
ES 中使用的这种方法,假定冲突时不可能发生的,并且不会阻塞正在尝试的操作。然而,如果数据在读写当中被修改,更新将会失败。应用程序接下来决定该如何解决冲突。例如,可以重试更新,使用新的数据,或者将相关情况报告给用户。
ES 是分布式的,当文档创建、更新和删除时,新版本的文档必须复制到集群中的其他节点。ES 也是异步和并发的,这意味着这些复制请求被并行发送,并且达到目的时也许顺序是乱的。ES 需要一种方法确保文档的旧版本不会覆盖新的版本。
每个文档都有一个_version(版本)号,当文档被修改时版本号递增。ES 使用这个 version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
以下演示 ES 对于版本号的使用
# 查询数据
curl --location --request GET 'http://10.240.30.93:9200/user/_doc/l76GEIEBBfzVdzdvUxQl'
{
"_index": "user",
"_type": "_doc",
"_id": "l76GEIEBBfzVdzdvUxQl",
"_version": 1, // 默认版本
"_seq_no": 0, // 序列号
"_primary_term": 3,
"found": true,
"_source": {
"name": "zhangsan"
}
}
# 修改数据
curl --location --request POST 'http://10.240.30.93:9200/user/_update/l76GEIEBBfzVdzdvUxQl' \
--header 'Content-Type: application/json' \
--data-raw '{
"doc":{
"name": "zhangsan1"
}
}'
{
"_index": "user",
"_type": "_doc",
"_id": "l76GEIEBBfzVdzdvUxQl",
"_version": 2, // 版本递增
"result": "updated",
"_shards": {
"total": 3,
"successful": 1,
"failed": 0
},
"_seq_no": 1, // 序列号也增加了
"_primary_term": 3 // 主分片次数
}
# 如果针对以上有并发修改,都修改name字段,我们可以指定 if_seq_no 和 if_primary_term 修改
curl --location --request POST 'http://10.240.30.93:9200/user/_update/l76GEIEBBfzVdzdvUxQl?if_seq_no=1&if_primary_term=3' \
--header 'Content-Type: application/json' \
--data-raw '{
"doc":{
"name": "zhangsan2"
}
}'
# 当然我们也可以指定他的版本,但是version的值一定要比原值高
curl --location --request POST 'http://10.240.30.93:9200/user/_doc/l76GEIEBBfzVdzdvUxQl?version=4&version_type=external' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "zhangsan2"
}'
{
"_index": "user",
"_type": "_doc",
"_id": "l76GEIEBBfzVdzdvUxQl",
"_version": 4,
"result": "updated",
"_shards": {
"total": 3,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 3
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
_seq_no
严格递增的顺序号,每个文档一个,Shard 级别严格递增,保证后写入的 Doc 的_seq_no 大于先写入的 Doc 的_seq_no。
任何类型的写操作,包括 index、create、update 和 Delete,都会生成一个_seq_no。
每个文档在使用 Lucene 的 document 操作接口之前,会获取到一个_seq_no,这个_seq_no 会以系统保留 Field 的名义存储到 Lucene 中,文档写入 Lucene 成功后,会标记该 seq_no 为完成状态,这时候会使用当前 seq_no 更新 local_checkpoint。_primary_term
_primary_term 也和_seq_no 一样是一个整数,每当 Primary Shard 发生重新分配时,比如重启,Primary 选举等,_primary_term 会递增 1。
_primary_term 主要是用来恢复数据时处理当多个文档的_seq_no 一样时的冲突,避免 Primary Shard 上的写入被覆盖。