如何减少 Elasticsearch 集群中的分片数量

作者:来自 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 APIReindex 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. 创建一个具有更少分片的新索引

  2. 创建一个同时搜索旧索引和新索引的别名

  3. 将所有写入流量重定向到新索引

  4. 启动从旧索引到新索引的重新索引过程

  5. 完成后,删除别名并删除旧索引

这种方法确保数据零丢失,同时避免了双写或多次重新索引的复杂性。

步骤 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. 创建一个具有更少分片的新索引(同上文步骤 1)

  2. 启动从旧索引到新索引的重新索引过程(同上文步骤 4)。记录开始此步骤的时间戳

  3. 完成后,将所有写入流量重定向到新索引(同上文步骤 3)

  4. 创建一个别名,使其可以同时搜索两个索引(同上文步骤 2)。注意,这允许在下一步重新索引期间,新索引到旧索引的文档仍可被搜索,但有些文档可能暂时在搜索结果中出现两次

  5. 运行第二次重新索引操作,将新索引的文档复制过来,使用下面的命令并使用步骤 2 中记录的时间戳:

    复制代码
    POST _reindex
    {
      "source": {
        "index": "log-1",
        "query": {
          "range": {
            "@timestamp": {
              "gte": "2025-01-28T10:00:00"
            }
          }
        }
      },
      "dest": {
        "index": "log-shrink-1",
        "op_type": "index"
      }
    }
  6. 当第二次重新索引操作完成后,你可以删除旧索引和别名(同上文步骤 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

相关推荐
顧棟5 小时前
【ES实战】ES6.8到9.1.4的常用客户端变化
elasticsearch
Logintern095 小时前
只有通过Motor 获取 mongodb的collection,才能正常使用 async with collection.watch()监听集合变更
数据库·mongodb
知识浅谈6 小时前
Elasticsearch 核心知识点全景解读
大数据·elasticsearch·搜索引擎
huaqw006 小时前
Java17新特性解析深入理解SealedClasses的语法约束与设计哲学
数据库
武子康6 小时前
大数据-120 - Flink滑动窗口(Sliding Window)详解:原理、应用场景与实现示例 基于时间驱动&基于事件驱动
大数据·后端·flink
Hello.Reader6 小时前
Flink 广播状态(Broadcast State)实战从原理到落地
java·大数据·flink
ApacheSeaTunnel6 小时前
从小时级到分钟级:多点DMALL如何用Apache SeaTunnel把数据集成成本砍到1/3?
大数据·开源·数据集成·seatunnel·技术分享
数据要素X6 小时前
寻梦数据空间 | 路径篇:从概念验证到规模运营的“诊-规-建-运”实施指南
大数据·人工智能·数据要素·数据资产·可信数据空间
big-data16 小时前
Paimon系列:主键表流读之changelog producer
大数据