作者:来自 Elastic Alex Salgado

在本综合指南中了解 Elasticsearch 分片如何影响集群性能,包括如何获取分片数量、更改默认值以及在需要时减少分片数量。
更多阅读:Elasticsearch 中的一些重要概念: cluster, node, index, document, shards 及 replica
刚接触 Elasticsearch?参加我们的 Elasticsearch 入门网络研讨会吧。你也可以立即开始免费的云试用,或在本地尝试 Elastic。
Elasticsearch 减少分片数量 ------ 说明与代码示例
当集群(cluster)中分片(shards)过多时,可以采取一些步骤来减少分片数量。本指南介绍了删除或关闭索引(indices)以及重新索引到更大索引的方法。下面我们将说明如何减少新建索引的分片数量、如何减少已存在索引的分片数量、如何减少主分片数量,以及如何减少基于时间的索引的分片数量。
如何减少新建索引的分片数量
在创建新索引时,应确保新建分片数量配置为尽可能小。
以常见的日志存储场景为例。自 Elasticsearch 7.0 起,默认每个索引有 1 个主分片,这已经是一个显著改进。然而,即使在这种更保守的配置下,你仍可以根据实际数据量进行优化。一个分片通常可以在保持良好性能的情况下容纳 10 到 50GB 的数据,因此如果你生成的数据量低于这个范围,可以保持默认的 1 个分片。对于更大的数据量,可以遵循每 10--30GB 一个分片的一般原则,并根据搜索与索引性能需求进行调整。
最简单的方式是创建一个索引模板并将其存储在集群状态中。以下请求将创建一个名为 "template_1" 的索引模板。它会应用到所有以 "log" 开头的新建索引名称上,并将每个索引的主分片数量设置为 1。
PUT _index_template/template_1
{
"index_patterns": ["log*"],
"template": {
"settings": {
"index.number_of_shards": 1,
"index.number_of_replicas": 1 // Default is 1 if not specified
}
}
}
注意 :如果你没有在模板中指定
number_of_replicas
,Elasticsearch 将使用默认值------每个主分片 1 个副本。你可以根据容错和性能需求进行调整。
具体代码语法可能会因所使用的 Elasticsearch 版本而有所不同,请参考创建索引模板的文档。
如何减少现有索引的分片数量
减少副本分片数量
减少分片数量的一个简单方法是降低副本数量。更改副本数量可以通过单个请求动态完成,只需几秒钟。
通常建议每个索引有 1 个副本分片,即每个分片在另一个节点上保留一份副本(除非你有大量并行搜索请求)。副本用于故障切换保护和提升搜索性能。如果包含主分片的节点宕机,其中一个副本会被提升为主分片,确保数据在搜索和新索引操作中保持可用。副本还能提高集群的弹性,并更均匀地分配负载。
不过,副本策略取决于集群的瓶颈。如果集群因大量索引操作而过载,将副本数量减少为 0 可以快速缓解压力。反之,如果集群在搜索性能上有瓶颈且容量充足,增加副本反而能帮助分担搜索负载。需要注意的是,将副本数设为 0 会失去容错能力,不应长期保持这种设置。
记住,副本数量也可以在前面展示的索引模板中进行配置。
以下请求会将所有以 "log" 开头的索引的副本分片数量设置为 0:
PUT log*/_settings
{
"index" : {
"number_of_replicas" : 0
}
}
减少主分片数量
每个索引的主分片数量无法动态更改 ------ 它是不可变的。用于在多个分片之间分配新文档的路由算法依赖于主分片总数。因此,减少现有索引的主分片数量会稍微复杂一些。
基本上,你需要创建一个新索引并复制所有数据。可以使用两种 API 来实现:Shrink API 和 Reindex API。这两种 API 各有优缺点,适用性取决于你的配置和需求。Shrink API 速度更快,但只能用于只读索引(即不再更新或插入文档的索引);Reindex API 则可用于活动索引。
使用 Shrink API 减少分片数量
你可以根据索引大小将分片过多的索引缩减为更少的分片数量 ------ 请参考相关的大小调整文档以获取指导。可缩减到的分片数量有一定限制,请务必阅读文档以确认此选项是否适合你的情况。
请注意,缩减索引需要一定的维护时间。首先,索引的所有主分片必须分配到同一节点上。请确保该节点有足够的存储空间(如果没有,请考虑使用下文所示的 Reindex API)。然后,需要将索引标记为只读。
以下请求将把所有以 "log" 开头的索引设置为只读,并将所有分片移动到名为 "shrink_node_name" 的节点上。
警告: 不要在正在写入的索引上运行此命令。将索引设置为只读会立即阻止所有写入操作,导致索引失败。此方法仅适用于不再接收新数据的索引。
PUT log*/_settings
{
"settings": {
"index.blocks.write": true,
"index.routing.allocation.require._name": "shrink_node_name"
}
}
你需要使用下面的命令逐个缩减索引。分片分配会恢复为默认状态,索引也将重新变为可写。
POST log-1/_shrink/log-shrink-1
{
"settings": {
"index.routing.allocation.require._name": null,
"index.blocks.write": null
}
}
具体语法可能会因所使用的 Elasticsearch 版本而有所不同,请参考 Shrink index API 文档。
使用 Reindex API 减少分片数量
如果要缩减的索引仍在被写入,可以使用 Reindex API。
Reindex API 可用于将文档分批从一个索引复制到另一个索引。在这种情况下,所有需要在索引时执行的工作都必须重新执行。这使得使用 Reindex API 比直接复制整个分片更慢,但仍是相对较快的过程。
Reindex API 会创建源索引的一个时间点视图,并将其保存在临时搜索上下文中,然后从该上下文中复制所有文档。在重新索引过程中创建的文档不会被复制,因为它们不存在于该上下文中。
有两种方法可以进行操作。第一种方法只需要一次重新索引操作,第二种需要两次。以下是这两种方法的工作方式。
在第一种方法中,你需要在开始重新索引之前,先将所有索引写入流量重定向到新索引。步骤如下:
-
创建一个具有更少分片的新索引
-
创建一个同时搜索旧索引和新索引的别名
-
将所有写入流量重定向到新索引
-
启动从旧索引到新索引的重新索引过程
-
完成后,删除别名并删除旧索引
这种方法确保数据零丢失,同时避免了双写或多次重新索引的复杂性。
步骤 1: 创建具有所需分片数量的目标索引:
PUT log-shrink-1
{
"settings": {
"index.number_of_shards": 1,
"index.number_of_replicas": 1
}
}
步骤 2: 创建一个别名,使其可以同时搜索两个索引:
POST _aliases
{
"actions": [
{
"add": {
"index": "log-1",
"alias": "logs-search"
}
},
{
"add": {
"index": "log-shrink-1",
"alias": "logs-search"
}
}
]
}
步骤 3: 将所有写入流量重定向到新索引
注意:在你的应用程序中,将目标索引从 "log-1" 改为 "log-shrink-1" 即可
步骤 4: 将所有文档从旧索引复制到新索引:
POST _reindex
{
"source": {
"index": "log-1"
},
"dest": {
"index": "log-shrink-1",
"op_type": "index"
}
}
步骤 5: 重新索引完成后,删除别名并删除旧索引:
POST _aliases
{
"actions": [
{
"remove": {
"index": "log-1",
"alias": "logs-search"
}
}
]
}
DELETE log-1
第二种方法略有不同,索引写入流量仅在第一次重新索引操作完成后才重定向到新索引,这需要运行第二次重新索引操作,将第一次重新索引期间在旧索引中写入的新文档复制过来。步骤如下:
-
创建一个具有更少分片的新索引(同上文步骤 1)
-
启动从旧索引到新索引的重新索引过程(同上文步骤 4)。记录开始此步骤的时间戳
-
完成后,将所有写入流量重定向到新索引(同上文步骤 3)
-
创建一个别名,使其可以同时搜索两个索引(同上文步骤 2)。注意,这允许在下一步重新索引期间,新索引到旧索引的文档仍可被搜索,但有些文档可能暂时在搜索结果中出现两次
-
运行第二次重新索引操作,将新索引的文档复制过来,使用下面的命令并使用步骤 2 中记录的时间戳:
POST _reindex { "source": { "index": "log-1", "query": { "range": { "@timestamp": { "gte": "2025-01-28T10:00:00" } } } }, "dest": { "index": "log-shrink-1", "op_type": "index" } }
-
当第二次重新索引操作完成后,你可以删除旧索引和别名(同上文步骤 5)。
为基于时间的索引减少分片数量
如果你使用基于时间的索引名称,例如每日日志索引,但数据量不足,减少分片的一个好方法是改用每周或每月的索引模式。
你也可以按月、季度或年份将旧的只读索引进行分组。最简单的方法是使用 Reindex API。
为多租户索引减少分片数量
当你有多租户场景,每个客户一个索引时,减少分片看起来会更困难。
在多租户情况下,你可能有一些大客户,每个索引有一个或多个分片,大小看起来合适。同时,也会有许多客户的索引较小。
如果在这种配置下仍需要为每个客户保留一个索引,可以使用 Filtered Aliases。
使用 Filtered Aliases 减少分片数量
Filtered Aliases 不像 "普通" 索引别名那样常见,但它允许你为一个索引创建多个视图。
你可以不为每个租户或客户创建一个索引,而是为所有较小的租户创建一个索引,这些租户小到不值得单独索引。然后添加一个关键字或数字字段来区分这些租户(如客户名称或客户 ID)。
创建索引后,只需在该索引上创建一个过滤别名,并将租户名称作为别名即可。
应用程序不会注意到后台使用了过滤别名,它们只需使用别名即可。但你可以在一个索引中存储许多不同租户,避免分片过多。
重要考虑事项:这种方法可能会受 "嘈杂邻居" 影响 ------ 当不同租户共享同一分片时,更活跃的租户可能影响较安静租户的性能。监控租户的活动模式,并考虑将使用特性相似的租户分组。
前提是你为所有客户使用兼容的数据模型。
下面的请求展示了如何在索引上创建过滤别名:
POST _aliases
{
"actions":[
{
"add":{
"index":"small-customers",
"alias":"customer-1",
"filter":{
"term":{
"customer-id":"1"
}
}
}
}
]
}
在多租户环境中减少分片数量时,你可以为所有小型租户创建一个新索引,并使用 Reindex API 复制数据。如果需要添加新字段以区分租户,例如 "customer-id",可以使用 ingest pipeline 添加新字段。
不想担心分片吗? 如果管理分片数量、容量和优化对你来说太复杂,可以考虑 Elasticsearch Serverless------ 我们的托管产品,你可以专注于业务逻辑,而我们处理所有基础设施复杂性,包括优化的分片管理和自动扩缩容。
了解更多关于分片的信息
原文:https://www.elastic.co/search-labs/blog/elasticsearch-shards-reduce-change-count