Rollover API— Elastic Stack 实战手册

Rollover API— Elastic Stack 实战手册

· 更多精彩内容,请下载阅读全本《Elastic Stack实战手册》

· 加入创作人行列,一起交流碰撞,参与技术圈年度盛事吧

创作人:杨松柏

了解 Elasticsearch 的同学应该都知道,索引的主分片在设定之后,改变(shrinksplitreindex)主分片数目的成本相当大;因此在设计之初,一定要规划好索引的分片数目。如果集群的中节点数目固定,且写入的数据不会再有更新操作或者更新操作极其少;可以使用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。代表要被创建和分配索引别名的目标索引名称;

目标索引名称必须遵循以下规则:

  1. 所有字符必须小写
  2. 不允许包含\, /, *, ?, ", <, >, |, (space character), ,, #
  3. 在7.0版本之前允许包含:,在7.0+不再支持
  4. 不能够以-_+开头
  5. 名称不能够是.或者..
  6. 长度不能超过 255 字节(注意:这是字节数限制,如果是字符得看表示一个字符需要多少个字节)
  7. 名称以 . 开头已经过时;除了隐藏索引和由插件管理的内部索引

如果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值的单位可为dhmsmsmicrosnanos,默认值为30s。 等待连接到主节点的时间。如果在超时时间阈值之前没有收到响应,则请求失败并返回错误。

timeout

可选参数,value 值的单位可为dhmsmsmicrosnanos,默认值为30s。请求等待响应的时间。如果在超时时间阈值之前没有收到响应,则请求失败并返回错误。

请求体参数

aliases

可选参数,alias object。包含索引的 Index aliases。详情可以参看索引别名,批量操作的相关内容(即_aliases API)。

conditions

可选参数,参数类型object。如果设置条件,则只有现有索引达到条件集任何一个条件的阈值才会完成rollover。如果省略,则无条件执行rollover。条件集包括:

  • max_age

    可选参数,value 值的单位可为dhmsmsmicrosnanos;依据索引的创建时间与现在时间差值作为阈值。

  • max_docs

    可选参数,参数类型为integer;索引的最大文档数目;计数不包括自上次refresh之后新添加的文档和副本分片的文档。

  • max_size

    可选参数, byte units 值(单位bkbmbgbtbpb)。索引大小的最大值,只计算索引主分片的大小,副本不包括在内。

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为别名,且别名和索引一对一关系;滚动请求过程如下:

  1. 创建一个新索引
  2. 给新索引添加别名
  3. 将别名与之前绑定索引的关联关系移除

别名绑定多个索引

rollover target为别名,且与多个索引建立了绑定关系,只有其中一组的绑定关系**is_write_index**的值为true(如果对**is_write_index**有疑问,请参看索引别名小节)。在这种情况下,滚动请求过程如下:

  1. 创建一个新索引
  2. 新索引于别名的绑定时,将is_write_index设置为true
  3. 修改别名与旧索引的绑定关系,将is_write_index设置为false

rollover-target 为数据流

rollover target为数据流,滚动请求过程如下:

  1. 创建一个新索引
  2. 在数据流上添加新索引作为备份索引和写索引
  3. 增加数据流的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-000001backing索引。

查看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 进行查看

Rollover API— Elastic Stack 实战手册

目标索引设定 setting

新索引的settingmappingaliases可以取自索引名匹配的任何索引模板。如果<rollover-target>为索引别名,你可以在 rollover API的请求体中指定settings, mappingsaliases,与创建索引的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;

Rollover API— Elastic Stack 实战手册

我们可以通过 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
上一篇:企业搜索-Elastic Stack 实战手册


下一篇:Search template — Elastic Stack 实战手册