它们用来实现基于乐观锁的版本控制。
Elasticsearch跟踪上次操作的序列号(sequence number)和主项(primary term),以更改它存储的每个文档。通过GET API响应信息中的_seq_no和_primary_term字段返回序列号和主项。
_primary_term
在故障转移期间,每当不同的分片成为主分片时,primary term的值就会增加。这有助于解决重新联机的旧的主分片上发生的更改,与新的主分片上发生的更改的冲突问题,一般新的主分片会获胜。
这里我们要先理解Elasticsearch的高性能和高可用机制:一个索引的数据可以存储到多个分片上,以均衡负载,提升读写性能;而每个分片(主分片)又可以有多个副本(副分片),在主分片脱机后Elasticsearch可以快速把其中一个副分片推举为新的主分片,保障Elasticsearch集群的高可用性。
副分片的primary term只是主分片更改次数的计数器。
这些primary term是递增的,并且在主分片升级时会发生变化。它们作为集群状态的一部分被持久化,因此代表了集群所在的主分片的一种“版本号”或“年代(generation)”。
为了确保文档的旧版本不会覆盖新版本,对文档执行的每个操作都由对应更改的主分片分配一个序列号。
假设你的索引由5个主分片组成(在Elasticsearch 7之前是默认的)。索引(新增文档)和更新的请求是针对主分片执行的。如果你有多个主分片,Elasticsearch能够将传入请求(例如,巨大的批量插入文档的请求)并行化/分发到多个分片,以提高性能。
因此,_primary_term字段提供了关于执行或协调更改主分片(本例中为#1、#2、#3、#4或#5)的信息。
_seq_no
序列号在Elasticsearch 6.0.0中引入。
一旦我们对primary term进行了保护,我们就添加了一个简单的计数器,并开始向每个操作发出该计数器的序列号。因此,这些序列号使我们能够理解发生在主分片上的对索引的操作。
_version字段存储了一个序列号,用于统计文档更新的次数。_seq_no字段也是一个序列号,用于统计索引上发生的操作次数。因此,如果你创建第二个文档,你将看到该文档的_seq_no和第一个文档的_seq_no有所不同。
实例
创建多个文档:
POST test/_doc/_bulk
{"index": {}}
{"test": 1}
{"index": {}}
{"test": 2}
{"index": {}}
{"test": 3}
返回响应信息:
{
"took" : 166,
"errors" : false,
"items" : [
{
"index" : {
"_index" : "test",
"_type" : "_doc",
"_id" : "d2zbSW4BJvP7VWZfYMwQ",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "test",
"_type" : "_doc",
"_id" : "eGzbSW4BJvP7VWZfYMwQ",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "test",
"_type" : "_doc",
"_id" : "eWzbSW4BJvP7VWZfYMwQ",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1,
"status" : 201
}
}
]
}
正如你所看到的:
- 对于所有文档,版本号_version为1;
- 对于文档1,_seq_no为0(第一次索引操作);
- 对于文档2,_seq_no为1(第二次索引操作);
- 对于文档3,_seq_no为2(第三次索引操作)。