Elasticsearch:通过 shrink API 减少 shard 数量来缩小 Elasticsearch 索引

通过使用 Shrink API 使用更少的主碎片来调整 Elasticsearch 索引的大小。在 Elasticsearch 中,每个索引都包含多个分片,而 Elasticsearch 集群中的每个分片都有助于使用cpu,内存,文件描述符等。这无疑有助于并行处理的性能。 以时间序列数据为例,你将对带有当前日期的索引进行大量读写。

如果该索引下降了请求,并且仅时不时地从该索引中读取数据,那么我们不再需要那么多分片,并且如果我们有多个索引,它们可能会建立并占用大量的计算能力。

对于要减少索引大小的情况,可以使用 Shrink API 减少主分片的数量。

这个过程和我之前介绍的文章 “Split index API - 把一个大的索引分拆成更多分片”刚好相反。在那篇文章中,我们是把一个索引拆分为多个。


Shrink API 介绍

Shrink index API 使你可以使用较少的主碎片将现有索引收缩为新索引。 目标索引中请求的主分片数量必须是源索引中的分片数量的一个分数。 例如,可以将具有8个主碎片的索引缩减为4、2或1个主碎片,或者将具有15个主碎片的索引缩减为5、3或1。如果索引中的分片数是质数,则可以 仅缩小为一个主碎片。 在收缩之前,我们必须满足一下的几个条件:

  • 索引中每个分片的(主副本或副本副本)必须存在于同一节点上。
  • 索引必须是 read-only
  • 索引的健康状态必须是绿色,请查看 health status

简单地说,我们可以通过如下的命令来实现:

PUT /my_source_index/_settings
{
  "settings": {
    "index.number_of_replicas": 0,       (1)                         
    "index.routing.allocation.require._name": "shrink_node_name", (2) 
    "index.blocks.write": true  (3)                                  
  }
}

在上面:

  1. 删除索引的所有备份
  2. 把索引的分片重新分配到一个叫做 shrink_node_name 的节点。可以具体参阅文章 “运用shard filtering来控制索引分配给哪个节点” 把主副分片分配于同一个节点上,或者链接  Index-level shard allocation filtering.
  3. 禁止对此索引进行写操作。 仍然允许元数据更改,例如删除索引。

Shrink 步骤

使用与源索引相同的定义创建目标索引,但主碎片数量较少。 然后,它将段从源索引硬链接到目标索引。 最后,它恢复目标索引,就好像它是刚刚重新打开的封闭索引一样。

 

动手实践

我们首先按照之前的文章 “Split index API - 把一个大的索引分拆成更多分片” 把一个索引分拆为两个索引。

GET _cat/shards/kibana_sample_data_logs_split?v

上面的命令显示:

index                         shard prirep state   docs store ip        node
kibana_sample_data_logs_split 1     p      STARTED 7076 4.8mb 127.0.0.1 node1
kibana_sample_data_logs_split 0     p      STARTED 6998 4.7mb 127.0.0.1 node1

这个 kibana_sample_data_logs_split 索引有两个 primary shard。我们该如何把这个已经被拆分的索引重新变为只有一个 primary shard。当然具体的数量,可以按照你自己的要求来定义,只要符合上面的数量的要求描述。

按照上面的要求,我们需要把该索引集中到一个节点上。为此,我们可以通过如下的方式来获得索引 kibana_sample_data_logs_split 所在的 node 的名称:

GET _cat/shards/kibana_sample_data_logs_split?v

上面的命令显示:

index                         shard prirep state   docs store ip        node
kibana_sample_data_logs_split 1     p      STARTED 7076 4.8mb 127.0.0.1 node1
kibana_sample_data_logs_split 0     p      STARTED 6998 4.7mb 127.0.0.1 node1

上面显示 kibana_sample_data_logs_split 处于 node1 节点上。当然在我们的实际的使用中,这个节点会有不同,同时,还会有不同的副本的显示在不同的节点上,比如就像如下的一个结构:

index               shard prirep state   docs  store ip       node
my-index-2019.01.10 2     p      STARTED  193  101mb x.x.x.x  Lq9P7eP
my-index-2019.01.10 2     r      STARTED  193  101mb x.x.x.x  F5edOwK
my-index-2019.01.10 4     p      STARTED  197  101mb x.x.x.x  Lq9P7eP
my-index-2019.01.10 4     r      STARTED  197  101mb x.x.x.x  F5edOwK
my-index-2019.01.10 3     r      STARTED  184  101mb x.x.x.x  Lq9P7eP
my-index-2019.01.10 3     p      STARTED  184  101mb x.x.x.x  F5edOwK
my-index-2019.01.10 1     r      STARTED  180  101mb x.x.x.x  Lq9P7eP
my-index-2019.01.10 1     p      STARTED  180  101mb x.x.x.x  F5edOwK
my-index-2019.01.10 0     p      STARTED  187  101mb x.x.x.x  Lq9P7eP
my-index-2019.01.10 0     r      STARTED  187  101mb x.x.x.x  F5edOwK

按照上面的要求,我们执行如下的命令:

PUT kibana_sample_data_logs_split/_settings
{
  "settings": {
    "index.number_of_replicas": 0,
    "index.routing.allocation.require._name": "node1",
    "index.blocks.write": true
  }
}

上面的命令将删除索引的副本,把所有的主分片分配到 node1 的节点,并同时禁止写入。我们安装如下的方式来查询所有的分片的情况:

GET _cat/shards/kibana_sample_data_logs_split?v

上面显示的结果为:

index                         shard prirep state   docs store ip        node
kibana_sample_data_logs_split 1     p      STARTED 7076 4.8mb 127.0.0.1 node1
kibana_sample_data_logs_split 0     p      STARTED 6998 4.7mb 127.0.0.1 node1

显然所有的分片都已经在 node1 上。

接下来,我们使用 _shrink index API 接口来对索引进行缩小:

POST kibana_sample_data_logs_split/_shrink/kibana_sample_data_logs_shrink
{
  "settings": {
    "number_of_replicas": 0,
    "number_of_shards": 1,
    "index.codec": "best_compression",
    "index.routing.allocation.require._name": null, 
    "index.blocks.write": null 
  },
  "aliases": {
    "my_search_indices": {}
  }
}

在上面,我们清除了对索引的分配要求,并同时允许写入。运行完上的命令后,我们可以通过如下的方式来进行查询过程:

GET _cat/recovery/kibana_sample_data_logs_shrink?human&detailed=true

我们可以查看到如下的信息:

kibana_sample_data_logs_shrink 0 345ms local_shards done n/a n/a 127.0.0.1 node1 n/a n/a 0 0 100.0% 30 0 0 100.0% 10093464 0 0 100.0%

我们可以看到完成度为 100%。

我们通过如下的命令来查看 kibana_sample_data_logs_shrink 的 primary shard 的数量:

GET _cat/shards/kibana_sample_data_logs_shrink?v

上面的命令:

index                          shard prirep state    docs store ip        node
kibana_sample_data_logs_shrink 0     p      STARTED 14074 9.6mb 127.0.0.1 node1

我们查看一下这个所的文档数量:

GET kibana_sample_data_logs_shrink/_count

上面的结果显示:

{
  "count" : 14074,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

这个显然和我们最原始的文档的数量是一样的。

在很多的情况下,我们甚至可以把该索引的 segments 的数量降为1。我们可以通过如下的方法来做:

POST kibana_sample_data_logs_shrink/_forcemerge?max_num_segments=1

上面的命令将返回:

{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  }
}

这样我们可以提高搜索的速度。