当您使用Elasticsearch集群出现索引分片设置不合理(例如索引主分片设置不合理、每个分片存在大量数据等)引发集群性能问题时,可通过_split API在线扩大主分片数,将现有索引拆分为具有更多主分片的索引。本文介绍如何通过_split API快速拆分主分片。

背景信息

索引创建后,Elasticsearch不支持修改索引主分片的数量,如果需要修改,一般会使用reindex重建索引,耗时太久。而在6.x版本开始,Elasticsearch支持在线扩大主分片数的Split index API,支持将现有索引拆分为具有更多主分片的索引。

reindex与_split API的性能测试信息如下:
  • 测试环境:
    • 数据节点:数量为5个,规格为8核16 GB。
    • 索引数据:数据量为183 GB。
    • 分片数:原主分片数为5,目标分片数为20,副本数为0。
  • 测试结果
    方式 耗时 资源占用
    reindex 2.5小时 集群中有大量的写QPS,索引所占节点资源高。
    _split API 3分钟 集群数据节点CPU使用率为78%左右,load_1m为10左右。

前提条件

  • Elasticsearch集群状态健康,且负载处于正常水位。
  • 根据集群数据节点个数、集群磁盘容量等因素,合理评估索引可拆分的分片数。详细信息请参见Shard评估
  • 待拆分的索引禁止写入,且拆分后生成的新索引不存在。
  • Elasticsearch集群有足够的磁盘空间存储拆分后生成的新索引。

操作步骤

  1. 登录目标阿里云Elasticsearch实例的Kibana控制台,根据页面提示进入Kibana主页。
    登录Kibana控制台的具体操作,请参见登录Kibana控制台
    说明 本文以阿里云Elasticsearch 7.10.0版本为例,其他版本操作可能略有差别,请以实际界面为准。
  2. 单击右上角的Dev tools
  3. Console中,执行如下命令,在创建索引时指定index.number_of_routing_shards,设置索引可拆分的分片数。
    以下示例以7.10版本实例为例,在创建dest1索引时指定分片路由数number_of_routing_shards,对主分片进行拆分。可拆分的主分片数需要为number_of_routing_shards的一个因数且为number_of_shards(主分片数)的倍数。如下number_of_shards为2,number_of_routing_shards为24,则可拆分的主分片数支持:4、6、8、12、24。
    说明 使用时需要将dest1替换为您的业务索引名。
    PUT /dest1
    {
      "settings": {
        "index": {
          "number_of_routing_shards": 24,
          "number_of_shards":2
        }
      }
    }
    参数 说明
    number_of_routing_shards 路由分片数,定义索引可拆分的次数或原始分片可拆分的分片数。创建索引指定该参数,要求索引主分片数必须是路由分片数的一个因数。
    说明
    • Elasticsearch 7.0以下版本,创建索引时必须指定index.number_of_routing_shards,最大值为1024;7.0及以上版本,index.number_of_routing_shards值默认依赖主分片数,如果创建索引时未指定,默认按因子2拆分,并且最多可拆分为1024个分片。例如原索引主分片数为1,则可拆分为1~1024中的任意数;原索引主分片为5,则支持拆分的分片数为:10、20、40、80、160、320以及最大数640(不能超过1024)。
    • 经过shrink后的主分片再进行split时,主分片数为原主分片数的倍数即可。例如原分片数为5,进行split时,支持拆分的分片数为:10、15、20、25、30...,最大不能超过1024。
    number_of_shards 索引的主分片数。
  4. 插入数据。
    说明 以下数据仅供测试。
    POST /dest1/_doc/_bulk
    {"index":{}}
    {"productName":"产品A","annual_rate":"3.2200%","describe":"可以自助选择消息推送"}
    {"index":{}}
    {"productName":"产品B","annual_rate":"3.1100%","describe":"每天收益到账消息推送"}
    {"index":{}}
    {"productName":"产品C","annual_rate":"3.3500%","describe":"每天收益立即到账消息推送"}
  5. 禁止对原索引的写入操作。
    PUT /dest1/_settings
    {
      "settings": {
        "index.blocks.write": true
      }
    }
  6. 拆分原索引并配置新索引,取消新索引的禁止写入限制。
    POST dest1/_split/dest3
    {
      "settings": {
        "index.number_of_shards": 12,
        "index.blocks.write": null
      }
    }
    以上示例使用_split API从原索引dest1拆分出新索引dest3,设置新索引的分片数为12,且取消新索引禁止写限制。
    注意
    • 由于原索引的主分片数为2,index.number_of_routing_shards为24,则拆分后生成的新索引的主分片数需要为原索引主分片数的整数倍且不能超过24,否则Kibana会报错。
    • split过程会进行segment merge操作,此操作会消耗集群计算资源,增加集群负载。因此在操作前需确保集群有充足的磁盘空间,并建议在业务低峰期操作。
    • 使用时需要将dest1dest3替换为您的业务索引名。
  7. 测试结果。
    通过_cat recovery API查看分片拆分进度,当无拆分分片相关的recovey,且集群状态健康,则分片拆分完成。
    • 查看分片拆分进度
      GET _cat/recovery?v&active_only

      当返回结果的index列没有待拆分的索引时,说明无拆分分片相关的recovey。

    • 查看集群健康状态
      GET _cluster/health

      当返回结果中包含"status" : "green"时,说明集群状态健康。

常见问题

Q:split完成后,为什么集群的CPU使用率、节点load_1m没有降下来?

A:split过程涉及到对文档进行重新路由,新索引会存在大量的docs.deleted文档。通过GET _nodes/hot_threads可看到索引在进行merge操作,所以计算资源负载会比较高,建议在业务低峰期操作。