1、乐观并发控制
Elasticsearch是分布式的,创建、更新或删除文档时,必须将 文档的新版本复制到集群中的其他节点。Elasticsearch也是异步和并发的,这意味着这些复制请求是并行发送的,并且可能不按顺序到 达目的地。Elasticsearch需要一种方法来确保旧版本的文档永远不 会覆盖新版本的文档。
为了确保旧版本的文档不会覆盖新版本文档,对文档执行的每个 操作都由主分片分配一个序列号,序列号随着每个操作的增加而增 加,因此新操作的序列号肯定比旧操作的序列号更高。然后, Elasticsearch可以使用这个序列号来确保新的文档版本不会被分配 了较小序列号的更改覆盖。
例如,以下索引命令将创建一个文档并为其分配初始序列号 _seq_no
和 _primary_term
( _primary_term 和 _seq_no 都是整 数),每当主分片发生重新分配时,比如重启、Primary选举等, _primary_term会递增1:
PUT products/_doc/1
{
"product":"r2d2",
"details":"A resourceful astromech droid"
}
在响应中包括分配的_seq_no和_primary_term:
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
Elasticsearch 跟 踪 上 次 操 作 的_seq_no
和_primary_term
, 以 更改其存储的每个文档。在GET API的响应中,会在_seq_no和 _primary_term字段中返回:
GET products/_doc/1
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"product" : "r2d2",
"details" : "A resourceful astromech droid"
}
}
搜索API可以通过设置seq_no_primary_term参数返回每个命中 文档的_seq_no和_primary_term:
GET products/_search
{
"seq_no_primary_term":true,
"query": {
"match": {
"details": "droid"
}
}
}
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.2876821,
"hits" : [
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1",
"_seq_no" : 0,
"_primary_term" : 1,
"_score" : 0.2876821,
"_source" : {
"product" : "r2d2",
"details" : "A resourceful astromech droid"
}
}
]
}
}
_seq_no和_primary_term唯一地标识一个变更。通过记下返回 的这两个值,可以确保仅在检索文档后没有对其进行其他更改的情况 下更改文档。这是通过设置索引API或删除API的if_seq_no和 if_primary_term参数来完成的。
例如,以下调用将确保向文档中添加标记tag,而不会丢失对描述 的任何潜在更改或由其他API添加其他标记:
POST products/_update/1?if_seq_no=0&if_primary_term=1
{
"doc": {
"tags": ["droid"]
}
}
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"product" : "r2d2",
"details" : "A resourceful astromech droid",
"tags" : [
"droid"
]
}
}
2、refresh参数
索引、更新、删除和批量API支持用refresh参数来控制请求所做 的更改何时对搜索可见。
允许的值如下:
- 空或true :操作发生后立即刷新相关的主分片和副本分片(不是整个索引),以便更新的文档立即显示在搜索结果中。这个设置需要仔细考虑和验证,因为会导致性能下降(从索引和搜索的角度来看)。
- wait_for :在返回结果之前,将等待刷新使请求所做的更改可见,这不会强 制立即刷新,而是等待刷新发生。Elasticsearch自动刷新,刷新频 率是index.refresh_interval,默认是1s。这个设置是动态的。调用 refresh API,或在响应的API上设置refresh为true都将导致刷新,请 求将返回。
- false(默认值) :不执行与刷新相关的操作。此请求所做的更改将在请求返回后的 某个时间点可见。
2.1、如何选择refresh的值
除非有充分的理由等待更改变为可见,否则始终使用 refresh=false。
如果必须使请求所做的更改与请求同步可见,当设置refresh为 true时,Elasticsearch将增加更多负载,设置为wait_for;将等待更 长时间,这需要结合实际情况决定。
2.2、强制刷新
如果当已经有index.max_refresh_listeners
值定义数量(默认 为1000个)的请求在该分片上等待刷新时,该请求的行为将如同 refresh设置为true一样:它将强制刷新。这保证了当该请求返回时,其更改对搜索可见,同时防止对被阻塞的请求使用未经检查的资源。 如果请求由于监听器槽用完而强制刷新,则其 响应将包含"forced_refresh":true
信息。
批量请求只占用它们接触的每个分片上的一个槽,不管它们修改 分片多少次。
例如,如下将请求创建文档并立即刷新索引,使其可见:
PUT /test/_doc/1?refresh
{"test":"test"}
PUT /test/_doc/2?refresh=true
{"test":"test"}
如下操作都将创建一个文档,但文档不会立即可见,请求立即返 回不会等待文档可见:
PUT /test/_doc/3
{"test":"test"}
PUT /test/_doc/4?refresh=fase
{"test":"test"}
如下操作将创建一个文档,并等待其在搜索时变为可见:
PUT /test/_doc/5?refresh=wait_for
{"test":"test"}