「必备技能」Elasticsearch索引全生命周期管理(附代码)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「必备技能」Elasticsearch索引全生命周期管理(附代码)相关的知识,希望对你有一定的参考价值。

参考技术A

索引(Index)是Elasticsearch中最重要的概念之一,也是整个Elasticsearch操作的基础,Elasticsearch提供了Index APIs用于Elasticsearch生命周期的管理,包括索引的创建、查询、删除和设置,索引的冻结和解冻、拆分和收缩等,掌握索引的管理是Elasticsearch开发、运维的基础能力,也有助于后期对于Elasticsearch的优化。

创建一个索引可以使用Elasticsearch提供的API,其格式如下:

其中 为要创建的索引的名称,是一个必须的参数,所有的字母都必须是小写形式。在创建索引的同时还可以进行相关的设置:

一个不做任何设置的,单纯创建索引的请求如下:

响应体中 “acknowledged” 为true代表索引已经成功在Elasticsearch集群中创建, “shards_acknowledged” 为true则代表在请求超时之前所有的索引分片的副本分片也全部准备完毕。当然,即使 “acknowledged” 和 "shards_acknowledged" 都为false,只能代表超时之前没有完成,后续集群还是可能最终成功创建了索引。

创建索引的API还支持Query Parameters和Request Body,其中的参数均是 可选的参数

Query Parameters支持以下参数:

Request Body也支持三个参数:

以下代码示例同时指定了Query Parameters和Request Body

通过Get index API,可以查询一个或者多个索引的相关信息,其API格式如下:

其中target可以是数据流、索引,还可以是一个别名,多个索引之间使用逗号分隔,target还支持模糊查询(*),如果查询所有的索引,可以使用 * 或者 _all 。如果集群开启了安全权限控制,那么要查询索引信息需要获得 view_index_metadata 或者 manage 的索引操作权限。以下为查询的具体案例:

Get index API支持带url查询参数,这些参数都是可选参数,主要有以下几个:

首先需要明确索引本身是不能被修改的,当我们说修改索引时,实际上是指修改索引的别名、字段映射(mapping)和配置(settings)。

首先说明一下别名的作用。Elasticsearch提供了别名的功能,一个别名可以添加多个索引,方便通过操作一个别名实际上操作到多个具体的索引,例如每年建一个索引,分别有index-2019,index-2020,index-2021,index-2022共4个索引,这4个索引都关联了别名index-year,当需要查询最近4年的数据的时候,可以使用index-year同时查询这4个年份的索引。

别名的修改,实际上是从索引上删除别名,再重新添加新的别名,将这两个动作放在一起,保证其原子性,确保不会存在别名被删除的瞬间产生没有指向任何索引的问题。

以下代码为将my-index从索引my-index-000001解除,然后重新绑定在索引my-index-000002上。

mapping指定了索引的数据结构,字段类型,修改索引的mapping,可以修改原有字段的名称、类型,添加新的字段等。以下为添加新的email字段,类型为keyword。

支持多个索引、data stream和别名,支持模糊搜索,使用 * 或者 _all 可以指定所有的data stream和别名。

以下为修改settings的案例,设置索引有2个主分片,2个副本。

删除索引将会删除其对应的文档、分片和集群中的元数据信息,索引的操作是一个高危操作,需要慎重,如果集群开启了安全权限控制,那么要删除索引,需要获得 delete_index 或者 manage 的索引操作权限。

不能删除数据流(data stream)的写索引,要删除当前写索引,必须对data stream进行rollover,创建新的写索引。

删除索引的API如下:

是必须参数,指定索引名称,多个索引可以用逗号分割,不支持使用别名,默认情况下也不支持使用模糊匹配,确实需要使用模糊匹配的,需要将集群参数 action.destructive_requires_name 设置为false。 Delete Index API类似Get Index API,同样支持URL查询参数,这些参数包括 allow_no_indices 、 expand_wildcards 、 ignore_unavailable 、 master_timeout 、 timeout ,含义类似,不再赘述。 以下是删除索引的案例代码:

默认情况下,索引一旦被创建出来就是打开的状态,但是在某些情况下可能需要关闭索引,例如一些老旧不需要使用到的索引,仍然占用一定的空间,并且集群仍然需要一定的维护开销,关闭索引将阻塞所有对这个索引的读/写操作,关闭的索引不必维护索引或搜索文档的内部数据结构,从而可以减少集群上开销。

特别注意:关闭索引的操作会消耗大量磁盘空间,在生产上也是一个需要特别注意的操作。可以通过集群设置API将 cluster.indices.close.enable 设置为false来禁用索引的关闭,默认为true。

当一个索引被打开或者关闭,master节点负责重启分片,这些分片都会经历recovery过程,打开或者关闭索引后都会自动进行分片数据的复制,以保证有足够的副本分片以保证高可用性。

默认情况下只支持匹配全名称的特定的索引,但是如果设置了参数 action.destructive_requires_name 为false,则可以使用*或者_all指代所有的索引,但这种情况下一旦有一个索引匹配失败则会报错,不建议开启。

open API用于重新打开被关闭的索引,如果目标是一个data stream,则打开这个data stream背后对应的所有的索引。

默认情况下只支持匹配全名称的特定的索引,但是如果设置了参数 action.destructive_requires_name 为false,则可以使用 * 或者 _all 指代所有的索引,但这种情况下一旦有一个索引匹配失败则会报错。

索引收缩和拆分是指收缩或者拆分索引的主分片数量。首先,我们要明白为什么需要进行索引的收缩和拆分呢?

在 elasticsearch 中,主节点管理分片是很大的工作量,降低集群整体分片数量可以降低 recovery 时间,减小集群状态的大小,降低集群的维护消耗。在很多情况下,经历了一段时间的运行以后,一些冷索引不会再有数据写入,将一些小分片合并可以降低集群的维护成本。

另一方面,如果在业务运行过程中发现前期的预估不足,业务量较大导致单个分片过大,则需要进行索引的拆分扩大主分片的数量。
一、索引收缩

Elasticsearch从5.0版本起提供了 shrink API,用于针对一些小索引缩小索引分片数。实际上并不对源索引进行操作,而是使用与源索引相同的配置创建一个新索引,仅仅降低分片数,索引分片缩小完成后,源索引可删除。

一个索引要能够被shrink API进行分片缩小,需要满足以下三个条件:

为了使分片分配更容易,可以先删除索引的复制分片,等完成了shrink操作以后再重新添加复制分片。

可以使用以下代码,实现删除所有的副本分片,将所有的主分片分配到同一个节点上,并且设置索引状态为只读:

重新分配源索引的分片可能需要一段时间,可以使用_cat API跟踪进度,或者使用集群 健康 API通过 wait_for_no_relocating_shards 参数等待所有分片完成重新分配。

当完成以上步骤以后就可以进行shrink操作了,以下为_shrink API的格式:

以下案例将索引my-index-000001缩小主分片到shrunk-my-index-000001索引。

特别注意:由于添加新文档时使用对分片数量取余获取目标分片,因此目标索引中请求的主分片数量必须是源索引中主分片数量的一个因子。例如,包含8个主分片的索引可以收缩为4个、2个或1个主分片,或者包含15个主分片片的索引可以收缩为5个、3个或1个主分片。如果索引中的分片数量是一个质数,那么它只能收缩为一个主分片。

如果当前索引是是一个data stream的写索引,则不允许进行索引收缩,需要对data stream进行rollover,创建一个新的写索引,才可以对当前索引进行收缩。

整个索引收缩的过程如下:

同样地,Elasticsearch还提供了Split API,用于将索引拆分到具有更多主分片的新索引。Split API的格式如下:

要完成整个Split的操作需要满足以下条件:

以下API请求可以将索引设置为只读:

如果当前索引是是一个data stream的写索引,则不允许进行索引拆分,需要对data stream进行rollover,创建一个新的写索引,才可以对当前索引进行拆分。

以下是使用Split API进行索引拆分的请求案例, Split API支持 settings 和 aliases 。

index.number_of_shards 指定的主分片的数量 必须是源分片数量的倍数。

索引拆分可以拆分的分片的数量由参数 index.number_of_routing_shards 决定,路由分片的数量指定哈希空间,该空间在内部用于以一致性哈希的形式在各个 shard 之间分发文档。 例如,将 number_of_routing_shards 设置为30(5 x 2 x 3)的具有5个分片的索引可以拆分为 以2倍 或 3倍的形式进行拆分。换句话说,可以如下拆分:

5 10 30(拆分依次为2和3)

5 15 30(拆分依次为3和2)

5 30(拆分6)

index.number_of_routing_shards 是一个静态配置,可以在创建索引的时候指定,也可以在关闭的索引上设置。其默认值取决于原始索引中主分片的数量,默认情况下,允许按2的倍数分割 最多1024 个分片。但是,必须考虑主分片的原始数量。例如,使用5个分片创建的索引可以被分割为10、20、40、80、160、320,或最多640个分片。

如果源索引只有一个主分片,那么可以被拆分成为任意数量的主分片。
2.2、索引拆分的工作过程

2.3、为什么Elasticsearch不支持增量的重新分片?

大多数的键值存储都支持随着数据的增长实现自动分片的自动扩展,为什么Elasticsearch不支持呢?

最经典的方案在于新增一个分片,然后将新的数据存储到这个新增加的分片,但是这种方案在Elasticsearch中可能会造成索引的瓶颈,整体的结构会变得比较复杂,因为Elasticsearch需要去定位文档到底归属于在哪一个分片,这意味着需要使用不同的哈希方案重新平衡现有数据。

键值存储系统解决这个问题的方案一般是使用一致性哈希,当分片数从N增加到N+1时,一致性哈希只需要对1/N的key进行重新分配,例如redis集群的方案就是蕾丝如此。

但是Elasticsearch分片的底层实际上上是Lucene的索引,而从Lucene索引删除一小部分的数据,通常比键值存储系统的成本要高得多。所以Elasticsearch选择在索引层面上进行拆分,使用硬链接进行高效的文件复制,以避免在索引间移动文档。

对于仅追加数据而没有修改、删除等场景,可以通过创建一个新索引并将新数据推送到该索引,同时添加一个用于读操作的涵盖旧索引和新索引的别名来获得更大的灵活性。假设旧索引和新索引分别有M和N个分片,这与搜索一个有M+N个分片的索引相比没有任何开销。

2.4、如何监控Split的进度

使用Split API进行索引拆分,API正常返回并不意味着Split的过程已经完成,这仅仅意味着创建目标索引的请求已经完成,并且加入了集群状态,此时主分片可能还未被分配,副本分片可能还未创建成功。

一旦主分片完成了分配,状态就会转化为 initializing ,并且开始进行拆分过程,直到拆分过程完成,分片的状态将会变成 active 。

可以使用 _cat recovery API 来监控Split进程,或者可以使用集群 健康 API通过将 wait_for_status 参数设置为黄色来等待所有主分片分配完毕。

Elasticsearch Clone API可以用于Elasticsearch索引数据的复制和备份。
二、索引克隆API

索引克隆不会克隆源索引的元数据,包括别名、ILM阶段定义、CCR follower相关信息,克隆API将会复制除了index.number_of_replicas和index.auto_expand_replicas之外的所有配置,这两个特殊的配置可以在克隆API的请求中显式指定。Clone API的格式如下:

索引满足可以克隆的条件是:

参照之前讲过的内容,仍然可以通过将 index.blocks.write 设置为true来确保索引是可读的状态。以下是克隆API的案例:

注意: index.number_of_shards 的值必须与源索引的主分片数量一致。
三、索引克隆的过程

四、监控克隆的进度

使用Clone API进行索引的克隆,API正常返回并不意味着Clone的过程已经完成,这仅仅意味着创建目标索引的请求已经完成,并且加入了集群状态,此时主分片可能还未被分配,副本分片可能还未创建成功。

一旦主分片完成了分配,状态就会转化为 initializing ,并且开始进行克隆过程,直到克隆过程完成,分片的状态将会变成 active 。

可以使用 _cat recovery API 来监控Clone进程,或者可以使用集群 健康 API通过将 wait_for_status 参数设置为黄色来等待所有主分片分配完毕。

rollover API是Elasticsearch提供的一个很好用的功能。我们都知道在mysql中一旦数据量比较大,可能会存在分库分表的情况,例如根据时间每个月一个表。rollover功能就类似这种情况,它的原理是先创建一个带别名的索引,然后设定一定的规则(例如满足一定的时间范围的条件),当满足该设定规则的时候,Elasticsearch会自动建立新的索引,别名也自动切换指向新的索引,这样相当于在物理层面自动建立了索引的分区功能,当查询数据落在特定时间内时,会到一个相对小的索引中查询,相对所有数据都存储在一个大索引的情况,可以有效提升查询效率。

rollover API会为data stream或者索引别名创建一个新的索引。(在Elasticsearch 7.9之前,一般使用索引别名的方式来管理时间序列数据,在Elasticsearch之后data stream取代了这个功能,它需要更少的维护,并自动与数据层集成)。

rollover API的效果依据待滚动的索引别名的情况不同而有不同的表现:

使用rollover API的时候如果指定新的索引的名称,并且原索引以“-”结束并且以数字结尾,那么新索引将会沿用名称并且将数字增加,例如原索引是 my-index-000001 那么新索引会是 my-index-000002 。

如果对时间序列数据使用索引别名,则可以在索引名称中使用日期来跟踪滚动日期。例如,可以创建一个别名来指向名为 的索引,如果在2099年5月6日创建索引,则索引的名称为 my-index-2099.05.06-000001 。如果在2099年5月7日滚动别名,则新索引的名称为 my-index-2099.05.07-000002 。

rollover API的格式如下:

rollover API也支持Query Parameters和Request Body,其中Query parameters支持 wait_for_active_shards 、 master_timeout 、 timeout 和 dry_run ,特别说一下 dry_run ,如果将 dry_run 设置为 true ,那么这次请求不会真的执行,但是会检查当前索引是否满足 conditions 指定的条件,这对于提前进行测试非常有用。

Request Body支持 aliases 、 mappings 和 settings (这三个参数只支持索引,不支持data stream)和 conditions 。

特别展开讲一下 conditions 。这是一个可选参数,如果指定了 conditions ,则需要在满足 conditions 指定的一个或者多个条件的情况下才会执行滚动,如果没有指定则无条件滚动,如果需要自动滚动,可以使用ILM Rollover。

conditions 支持的属性有:

以下为rollover一个data stream的一个案例:

响应信息如下:

以下为rollover一个索引别名的一个案例:

2. 请求rollover API

如果别名的索引名称使用日期数学表达式,并且按定期间隔滚动索引,则可以使用日期数学表达式来缩小搜索范围。例如,下面的搜索目标是最近三天内创建的索引。

索引的冻结是Elasticsearch提供的一个用于减少内存开销的操作,这个功能在7.14版本中被标记为 Deprecated ,在Version 8以后,已经对堆内存的使用进行了改进,冻结和解冻的功能不再适用。

这里简单地进行操作演示,如果是7.x版本仍然可以作为参照。

一、索引冻结

索引被冻结以后除了在内存中维护其元数据之外,在集群上几乎没有任何的开销。被冻结以后,索引是只读的,所有的写操作都将会被阻塞,例如文档的写入、merge合并等。

API格式如下:

以下为索引冻结操作的代码案例:

注意,冻结索引将关闭该索引,并在同一个API调用中重新打开它,这将导致在短时间内主分配没有被分配,并导致集群变为红色,直到再次完成分配。

二、索引解冻

对应索引的冻结,解冻的API格式如下:

以下为索引解冻操作的代码案例:

Elasticsearch提供了Resolve index API用于辅助进行索引的解析,根据提供的索引/别名/数据流的名称或者模式匹配,查询出当前集群中匹配的索引的信息。以下为API的格式:

案例如下:

这个API的作用多为辅助,实际使用不多,细节的参数可以参考官方的文档。

跟我一起奋斗成为Elastic专家

以上是关于「必备技能」Elasticsearch索引全生命周期管理(附代码)的主要内容,如果未能解决你的问题,请参考以下文章

一名全栈工程师的必备工具箱

必备 | Elasticsearch 8.X 最少必要知识视频(全)

必备 | Elasticsearch 8.X 最少必要知识视频(全)

ElasticSearch——索引生命周期管理

Elasticsearch使用索引生命周期管理实现热温冷架构

使用管道日期索引名称设置 ElasticSearch 索引生命周期策略的问题