· 更多精彩内容,请下载阅读全本《Elastic Stack实战手册》
创作人:杨松柏
了解 Elasticsearch 的同学应该都知道,索引的主分片在设定之后,改变(shrink
,split
,reindex
)主分片数目的成本相当大;因此在设计之初,一定要规划好索引的分片数目。如果集群的中节点数目固定,且写入的数据不会再有更新操作或者更新操作极其少;可以使用Rollover index
的方式来限制每个索引的大小。
Rollover:
若rollover-target
绑定的当前索引满足设定的条件,执行滚动操作将会为rollover-target
创建新索引。滚动目标可以是索引别名或者数据流;
- 当滚动目标是别名时,执行滚动别名将指向新的索引。
- 当滚动目标是数据流时,数据流将数据写入到新的索引,且新索引名后缀自增 1。
POST /alias1/_rollover/my-index-000002
{
"conditions": {
"max_age": "7d",
"max_docs": 2,
"max_size": "5gb"
}
}
如果需要实现 Rollover 自动化,可以自行实现一个定时任务请求该 API 或者使用 Elasticsearch 的 index lifecycle management (ILM) 功能。
API 介绍
Rollover API
POST /<rollover-target>/_rollover/<target-index>?wait_for_active_shards=<number>
POST /<rollover-target>/_rollover?wait_for_active_shards=<number>
URI 参数
必填参数,参数类型string
。将已存的索引别名或数据流,分配给目标索引,来完成执行滚动。
可选参数,参数类型string
。代表要被创建和分配索引别名的目标索引名称;
目标索引名称必须遵循以下规则:
- 所有字符必须小写
- 不允许包含
\
,/
,*
,?
,"
,<
,>
,|
,(space character)
,,
,#
- 在7.0版本之前允许包含:,在7.0+不再支持
- 不能够以
-
,_
,+
开头 - 名称不能够是
.
或者..
- 长度不能超过 255 字节(注意:这是字节数限制,如果是字符得看表示一个字符需要多少个字节)
- 名称以
.
开头已经过时;除了隐藏索引和由插件管理的内部索引
如果rollover-target
为数据流, <target-index>
参数是不被允许的;执行 Rollover 数据流会按照相应规则生产新的索引:格式为.ds-<rollover-target>-000001
,其中-000001
,数字必为6位数,左侧高位用0
补充,每发生一次 Rollover 自增加1。
如果rollover-target
为一个索引别名,且别名绑定的索引以-<number>
结尾;如果不指定<target-index>
执行 Rollover ,将生成新的索引indexName-<number>+1
。
如果别名现绑定的索引名称不满足以-<number>
形式结尾,则必须指定<target-index>
。
查询参数
dry_run
可选参数,参数类型为boolean
,默认值为 false。该参数的作用为检测 index 是否满足提供的 rollover 条件。 如果设置为true,将会完成检查工作,但不会真正执行 rollover 。
include_type_name
可选参数,参数类型为boolean
,默认值为 false。在 mappings
体内要求必须有mapping type。
wait_for_active_shards
可选参数,参数类型为 string
,默认值为 1(即只要一个主分片处于活跃便可以执行操作)。在执行rollover
,要求处于活跃状态的索引分片副本数目,以保证数据的可靠和安全性;设置为all
或任何正整数,最多为索引分片的总数(number_of_replicas+1
)。
详情可以参考 Active shards:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/docs-index_.html#index-wait-for-active-shards
master_timeout
可选参数,value
值的单位可为d
、h
、m
、s
、ms
、micros
、nanos
,默认值为30s
。 等待连接到主节点的时间。如果在超时时间阈值之前没有收到响应,则请求失败并返回错误。
timeout
可选参数,value 值的单位可为d
、h
、m
、s
、ms
、micros
、nanos
,默认值为30s
。请求等待响应的时间。如果在超时时间阈值之前没有收到响应,则请求失败并返回错误。
请求体参数
aliases
可选参数,alias object。包含索引的 Index aliases。详情可以参看索引别名,批量操作的相关内容(即_aliases API)。
conditions
可选参数,参数类型object
。如果设置条件,则只有现有索引达到条件集任何一个条件的阈值才会完成rollover
。如果省略,则无条件执行rollover
。条件集包括:
-
max_age
可选参数,value 值的单位可为
d
、h
、m
、s
、ms
、micros
、nanos
;依据索引的创建时间与现在时间差值作为阈值。 -
max_docs
可选参数,参数类型为
integer
;索引的最大文档数目;计数不包括自上次refresh
之后新添加的文档和副本分片的文档。 -
max_size
可选参数, byte units 值(单位
b
、kb
、mb
、gb
、tb
、pb
)。索引大小的最大值,只计算索引主分片的大小,副本不包括在内。
mappings
可选参数,参数 mapping object。为索引进行数据建模,定义字段schema
等。如果设置,mapping
可能包含:
- 字段名字(Field names)
- 字段的数据类型(Field data types)
- mapping中一些修饰参数(Mapping parameters)
settings
可选参数,参数 index setting object。索引的配置选项;比如设置滚动产生的新索引副本分片数目等等。
详情可以参看 Index Settings:https://www.elastic.co/guide/en/elasticsearch/reference/7.11/index-modules.html#index-modules-settings
Rollover的分类
依据不同rollover target
和别名情况,可以简单的分为三种执行情况:
别名只绑定一个索引
rollover target
为别名,且别名和索引一对一关系;滚动请求过程如下:
- 创建一个新索引
- 给新索引添加别名
- 将别名与之前绑定索引的关联关系移除
别名绑定多个索引
rollover target
为别名,且与多个索引建立了绑定关系,只有其中一组的绑定关系**is_write_index**
的值为true
(如果对**is_write_index**
有疑问,请参看索引别名小节)。在这种情况下,滚动请求过程如下:
- 创建一个新索引
- 新索引于别名的绑定时,将
is_write_index
设置为true
- 修改别名与旧索引的绑定关系,将
is_write_index
设置为false
。
rollover-target 为数据流
rollover target
为数据流,滚动请求过程如下:
- 创建一个新索引
- 在数据流上添加新索引作为备份索引和写索引
- 增加数据流的
generation
属性
实战示例
基础示例
别名与索引是一对一关系,执行 _rollover 之后,别名将关联到新的索引
PUT /logs-1 #备注1
{
"aliases": {
"logs_write": {}
}
}
# Add > 2 documents to logs-1
PUT logs-1/_bulk
{"index":{}}
{"user.id":"kimchy"}
{"index":{}}
{"user.id":"tom"}
POST /logs_write/_rollover #备注2
{
"conditions": {
"max_age": "7d",
"max_docs": 2,
"max_size": "5gb"
}
}
创建索引logs-1
且与别名logs_write
绑定.
logs_write
指向的索引满足条件集中的任何一个条件(索引“年龄值”>=7天或者主分片文档数目>=2或者主分片 size>=5gb);则logs-000002
将会被创建,且别名logs_write
将更新绑定到logs-000002
。
如上代码块 Rollover API 返回值如下
{
"acknowledged": true,
"shards_acknowledged": true,
"old_index": "logs-1",
"new_index": "logs-000002",
"rolled_over": true, #索引是否执行了滚动
"dry_run": false, # 是否为 dry_run 模式
"conditions": { # 条件集触发结果
"[max_age: 7d]": false,
"[max_docs: 2]": true,
"[max_size: 5gb]": false,
}
}
通过查看索引与别名的绑定关系,可以看到logs_write
只绑定logs-000002
# 查看命令
GET _cat/aliases/logs_write?v
#返回值如下
alias index filter routing.index routing.search is_write_index
logs_write logs-000002 - -
基于数据流滚动
数据流必须要提前设定一个索引模板,否则无法创建数据流。
PUT _index_template/template
{
"index_patterns": ["my-data-stream*"],
"data_stream": { }
}
创建数据流
PUT /_data_stream/my-data-stream # 备注1
创建一个名为my-data-stream
的数据流,并且初始化一个名字为my-data-stream-000001
的backing
索引。
查看GET /_data_stream/my-data-stream
。返回值如下
{
"data_streams" : [
{
"name" : "my-data-stream",
"timestamp_field" : {
"name" : "@timestamp"
},
"indices" : [
{
"index_name" : ".ds-my-data-stream-000001",
"index_uuid" : "Vir6yRm4S42k8n22mZ5YBw"
}
],
"generation" : 1,
"status" : "GREEN",
"template" : "my-index-template",
"ilm_policy" : "my-lifecycle-policy"
}
]
}
插入数据,然后执行滚动
# Add > 2 documents to my-data-stream
#为了能够实时的看到效果插入数据时,加上 refresh 参数
PUT my-data-stream/_bulk?refresh
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:21:15.000Z", "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" }
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:25:42.000Z", "message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" }
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:27:42.000Z", "message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" }
POST /my-data-stream/_rollover #备注1
{
"conditions" : {
"max_age": "7d",
"max_docs": 2,
"max_size": "5gb"
}
}
如果当前的写数据索引满足条件集中的任何一个条件;则滚动目标将创建一个新的backing
索引my-data-stream-000002
,且新的backing
索引将作为写入数据的索引。
上面代码块的 Rollover API 返回值如下:
{
"acknowledged" : false,
"shards_acknowledged" : false,
"old_index" : ".ds-my-data-stream-000001",#数据流之前对应的写数据索引
"new_index" : ".ds-my-data-stream-000002",,#数据流新对应的写数据索引
"rolled_over" : true,#索引是否执行滚动
"dry_run" : false, # 是否为 dry_run 模式
"conditions" : { # 滚动条件集,集触发情况
"[max_size: 5gb]" : false,
"[max_docs: 2]" : true,
"[max_age: 7d]" : false
}
}
再次查看数据流
{
"data_streams" : [
{
"name" : "my-data-stream", # 数据流名称
"timestamp_field" : {
"name" : "@timestamp"
},
"indices" : [
{
# backing索引
"index_name" : ".ds-my-data-stream-000001",
"index_uuid" : "Vir6yRm4S42k8n22mZ5YBw"
},
{
"index_name" : ".ds-my-data-stream-000002",
"index_uuid" : "WcctdbIqSMqk3MmefRdfDQ"
}
],
"generation" : 2, #当前是那一个版本的后备索引作为写索引
"status" : "GREEN",
"template" : "my-index-template", # 适配的索引模板
"ilm_policy" : "my-lifecycle-policy"
}
]
}
也可以通过 Kibana 进行查看
目标索引设定 setting
新索引的setting
、mapping
和aliases
可以取自索引名匹配的任何索引模板。如果<rollover-target>
为索引别名,你可以在 rollover API
的请求体中指定settings
, mappings
和aliases
,与创建索引的API(create index API)类似。如果请求体指定的值优先于匹配的索引模板。
如下示例重写了 index.number_of_shards setting:
PUT /logs-000001
{
"aliases": {
"logs_write": {}
}
}
POST /logs_write/_rollover
{
"conditions" : {
"max_age": "7d",
"max_docs": 2,
"max_size": "5gb"
},
"settings": {
"index.number_of_shards": 2
}
}
指定目标索引名称
如果<rollover-target>
是索引别名,并且绑定的索引名称以-<number>
结尾;如果不指定目标索引,发生滚动时生成的新索引名称为 indexName-6 位数字,每滚动一次数字自增一次。
例如索引为logs-1
(或者logs-000001
),执行滚动后新的索引名字为logs-000002
,6位数字低位自增高位补 0。
如果旧的索引名称不满足indexName-<number>
结尾,则执行滚动必须指定新索引名字;
示例如下:
# my_alias绑定的索引索引名称不满足indexName-<number>格式
#必须指定索引my_new_index_name
POST /my_alias/_rollover/my_new_index_name
{
"conditions": {
"max_age": "7d",
"max_docs": 2,
"max_size": "5gb"
}
}
基于 date math 滚动
如果<rollover-target>
是索引别名,使用 date math 根据索引滚动的当前日期来命名滚动索引;在某些场景中是非常有用的,比如定时按天删除索引、按天查询数据等。rollover API 支持 date math,但是要求索引名称必须以日期-<number>
结尾,
例如:logstash-2021.05.06-1
;以这种格式命名的主要是方便在任何时候执行滚动,索引名称自增。
示例如下:
# PUT /<logs-{now/d}-1> with URI encoding:
PUT /%3Clogs_%7Bnow%2Fd%7D-1%3E #备注1
{
"aliases": {
"logs_write": {}
}
}
PUT logs_write/_doc/1
{
"message": "a dummy log"
}
POST logs_write/_refresh
# 立即执行 _rollover
POST /logs_write/_rollover #备注2
{
"conditions": {
"max_docs": 1,
"max_age": "1d",
"max_size": "5gb"
}
}
创建一个索引取今天的日期,logs_2021.05.06-1
执行滚动操作,如果立即执行,则新索引名为logs_2021.05.06-000002
;如果等到24小时之后执行索引名称为logs_2021.05.07-000002
你可以按照 date math 文档的说明去使用这些索引。
例如,你需要搜索过去三天索引的数据,你可以按照如下的书写方式:
# GET /<logs-{now/d}-*>,<logs-{now/d-1d}-*>,<logs-{now/d-2d}-*>/_search
GET /%3Clogs-%7Bnow%2Fd%7D-*%3E%2C%3Clogs-%7Bnow%2Fd-1d%7D-*%3E%2C%3Clogs-%7Bnow%2Fd-2d%7D-*%3E/_search
Dry run
rollover API
支持dry_run
模式,该模式主要用于条件检测。比如想检测索引是否满足设定的条件完成滚动。
示例如下:
POST /logs_write/_rollover?dry_run
{
"conditions" : {
"max_age": "7d",
"max_docs": 1000,
"max_size": "5gb"
}
}
返回值如下:
{
"acknowledged" : false,
"shards_acknowledged" : false,
"old_index" : "logs-000002", #当前索引
"new_index" : "logs-000003",# 将要新生成的索引
"rolled_over" : false, #是否完成滚动
"dry_run" : true, # 是否为检测模式
"conditions" : {
"[max_size: 5gb]" : false,
"[max_docs: 1000]" : false,
"[max_age: 7d]" : false
}
}
基于 write index 滚动
基于write index
滚动主要是为了解决,使用别名查询能够获取到之前创建的索引数据,以及同一别名绑定多个索引滚动切换歧义问题。同一个别名具有查询和写入两种特性。
示例如下
#创建一个索引,绑定别名 logs,并标注 is_write_index 为 true,即通过别名写入数据,实际是写入到 my_logs_index-000001 表中
PUT my_logs_index-000001
{
"aliases": {
"logs": { "is_write_index": true }
}
}
#写入一个文档
PUT logs/_doc/1
{
"message": "a dummy log"
}
#刷新,生成一个 segements,使文档变得可搜索
POST logs/_refresh
#执行滚动操作
POST /logs/_rollover #备注1
{
"aliases": {"search_all": {}}, #备注2
"conditions": {
"max_docs": "1"
}
}
#再次基于别名写入
PUT logs/_doc/2
{
"message": "a newer log"
}
执行滚动操作write index
切换成为新创建的索引,之后写入的数据将写入到新索引中,滚动时新产生的索引添加别名 search_all
上述代码块执行_rollover
的返回值如下
{
"_index" : "my_logs_index-000002",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
在完成滚动之后,GET _alias/logs
,查看别名元信息,可以发现新索引已经作为write index
。
{
"my_logs_index-000002": {
"aliases": {
"logs": { "is_write_index": true }
}
},
"my_logs_index-000001": {
"aliases": {
"logs": { "is_write_index" : false }
}
}
}
基于 ILM 实现自动 Rollover
首先在 Kibana ,定义生命周期策略(也可通过 API 方式);
下图规定了 Hot 阶段重新生成索引的规则,这部分功能其实就是对于rollover API
的封装;下图中我们定义了一个名称为rollover-test-all
的生命周期管理策略,其中滚动条件为max_size
,为 20gb,max_age
为 1 天,max_docs
为 2;
我们可以通过 API,查看GET _ilm/policy/rollover-test-all
。返回值如下:
{
"rollover-test-all" : {
"version" : 1,
"modified_date" : "2021-05-11T15:23:27.155Z",
"policy" : {
"phases" : {
"hot" : {
"min_age" : "0ms",
"actions" : {
"rollover" : {
"max_size" : "20gb",
"max_age" : "1d",
"max_docs" : 2
},
"set_priority" : {
"priority" : 100
}
}
}
}
}
}
}
创建一个模板,模版中引用刚才定义的生命周期策略,并指定滚动的别名。
关于索引模板更多信息请参看 Simulate multi-component templates 。
# 这个是 legacy 的 index template,适用于 7.8 版本以前,
# 但在7.11版本,仍然可以用
PUT _template/iml-rollover_template
{
"index_patterns": [
"iml-rollover*"
],
"aliases": {
"iml-rollover_alias": {}
},
"settings": {
"index": {
"lifecycle": {
"name": "rollover-test-all",
"rollover_alias" : "iml-rollover_write_alias"
},
"refresh_interval": "2s",
"number_of_shards": "1",
"number_of_replicas": "0"
}
},
"mappings": {
"properties": {
"name": {
"type": "keyword"
}
}
}
}
# 7.8 版本之后,可以使用 _index_template 定义模板;
PUT _index_template/iml-rollover_template
{
"index_patterns": ["iml-rollover*"],
"template": {
"settings": {
"lifecycle": {
"name": "rollover-test-all",
"rollover_alias" : "iml-rollover_write_alias"
},
"refresh_interval": "2s",
"number_of_shards": "1",
"number_of_replicas": "0"
},
"mappings": {
"_source": {
"enabled": true
},
"properties": {
"name": {
"type": "keyword"
}
}
},
"aliases": {
"iml-rollover_alias": { }
}
},
"priority": 500,
"_meta": {
"description": "my custom rollover test"
}
}
创建索引
PUT iml-rollover-000001
{
"aliases": {
"iml-rollover_write_alias": { "is_write_index": true }
}
}
插入数据
PUT iml-rollover_write_alias/_bulk?refresh=true
{"index":{}}
{"name":"kimchy"}
{"index":{}}
{"name":"tom"}
查看索引情况GET _cat/indices/iml-rollover-*?v&h=health,index,docs.count
。
结果如下
health index docs.count
green iml-rollover-000001 2
green iml-rollover-000002 0
iml-rollover-000001 的文档个数为 2,并且 iml-rollover-000002 索引已经被创建。查看别名的关联关系GET _cat/aliases/iml-rollover_*?format=json
。索引 iml-rollover-000002 的别名 iml-rollover_write_alias 被标记为具有写权限。
[
{
"alias" : "iml-rollover_alias",
"index" : "iml-rollover-000001",
"filter" : "-",
"routing.index" : "-",
"routing.search" : "-",
"is_write_index" : "-"
},
{
"alias" : "iml-rollover_write_alias",
"index" : "iml-rollover-000001",
"filter" : "-",
"routing.index" : "-",
"routing.search" : "-",
"is_write_index" : "false"
},
{
"alias" : "iml-rollover_alias",
"index" : "iml-rollover-000002",
"filter" : "-",
"routing.index" : "-",
"routing.search" : "-",
"is_write_index" : "-"
},
{
"alias" : "iml-rollover_write_alias",
"index" : "iml-rollover-000002",
"filter" : "-",
"routing.index" : "-",
"routing.search" : "-",
"is_write_index" : "true"
}
]
和预期是一致的,正确的完成滚动。这时通过iml-rollover_write_alias
写入数据,数据被写入到iml-rollover-000002
索引中。再次执行插入数据语句,然后查看索引文档。
结果如下
health index docs.count
green iml-rollover-000001 2
green iml-rollover-000002 2