ES经典面试题:为什么主分片的数目不能修改?

前言

ES经典面试题:为什么主分片的数目不能修改?


一、实战演示

新建索引,尝试修改主分片。

DELETE my-index-000001

#创建索引,设置主分配数number_of_shards为4
PUT my-index-000001
{
   "settings": {
        "number_of_shards": "4",
        "number_of_replicas": "0"
  },
  "mappings": {
    "properties": {
      "company": {
        "type": "text", "analyzer": "ik_smart"
      }
    }
  }
}

#修改主分片数
PUT my-index-000001/_settings
{
  "number_of_shards": "2"
}

执行结果:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[my-index-000001/hQKoWSt9SlCiKkiyzMueNQ]]"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[my-index-000001/hQKoWSt9SlCiKkiyzMueNQ]]"
  },
  "status" : 400
}

说明:
索引的主分片数index.number_of_shards的值不能够动态设置,只能在索引创建的时候被指定。

二、路由算法

在回答问题之前,我们先说明ES数据写入过程中的路由算法。

ES的路由算法指的是根据routing和文档id计算目前分片ID shardid的过程。

一般情况下,路由计算方式为下面的公式:

shard_num = hash(_routing)  % num_primary_shards

默认情况下,_routing值就是文档id。

ES使用随机id和Hash算法来确保文档均匀地分配给分片。当使用自定义id或routing时,id或routing值可能不够随机,
造成数据倾斜,部分分片过大。在这种情况下,可以使用index.routing_partition_size配置来减少倾斜的风险。
routing_partition_size越大,数据的分布越均匀。

在设置了index.routing_partition_size的情况下,计算公式为:

shard_num =  (hash(_routing) + hash(_id)  % routing_partition_size )  % num_primary_shards

也就是说,_routing字段用于计算索引中的一组分片,然后使用_id来选择该组内的分片。

index.routing_partition_size取值应具有大于1且小于index.number_of_shards的值。

routing值是一个任意字符串,它默认是_id但也可以自定义,这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。这也解释了为什么主切片的数量只能在创建索引时定义且不能修改:如果主切片的数量在未来改变了,所有先前的路由值就失效了,文档也就永远找不到了。所有的文档API(get、index、delete、bulk、update、mget)都接收一个routing参数,它用来自定义文档到分片的映射。
自定义路由值可以确保所有相关文档.比如用户的文章,按照用户账号路由,就可以实现属于同一用户的文档被保存在同一分片上。

三、GET基本流程

通过GET请求读取单个文档的流程如下:
在这里插入图片描述
说明:

  1. 客户端向Node1发送读请求
  2. Node1使用文档ID来确定文档属于分片0,通过集群状态中的内容路由表信息获知分片0有三个副本数据,
    位于所有的三个节点中,此时它可以将请求发送到任意节点,这里它将请求转发到Node2.
  3. Node2将文档返回给Node1,Node1将文档返回给客户端。

Node1作为协调节点,会将客户端请求轮询发送到集群的所有副本来实现负载均衡。

根据文档ID获取文档信息的过程,核心是根据文档ID确定文档所属的分片Id。

四、问题解答

了解了ES中的分片路由算法和GET请求基本流程,现在我们可以来回答为什么ES中索引主分片的数目不能修改了。

在ES中索引数据写入时,需要根据主分片的数目和文档id来进行路由计算,确定文档保存的分片Id。
在根据文档Id读取单个文档数据时,也需要首先根据主分片的数目和文档id来进行路由计算,确定文档保存的分片Id。
如果在索引创建后,再对索引的主分片数进行修改 ,则会造成后续根据路由算法计算的分片id不准确,导致Get请求获取不到对应数据的问题。

核心:
主分片数目会影响分片路由计算的结果,导致数据查询失败。

五、索引膨胀问题

索引的主分片数目只能在创建索引时指定,且后续不能对主分片数目进行修改。
那么如果由于前期估算的误差,只给索引配置了的主分片数太少,而后续大量数据持续写入,就会造成
这些分片里面的数据不断增长,甚至出现一个分片几百G的情况。

针对这种索引主分片中数据膨胀的问题,我们该如何解决呢?
1、通过索引重建修改主分片数目
2、将数据保存到多个索引中,然后给索引指定相同的索引别名。
理论上来说,查询一个有8个分片的索引和2个具有4个分片的索引查询效率是一样的。

在这里插入图片描述

六、通过reindex索引重建修改索引主分片数目

对已有的索引,我们不能修改它的主分片数目。所以这里只能通过reindex索引重建来“曲线救国”。

索引重建的使用场景:
1、当你的数据量过大,而你的索引最初创建的分片数量不足,导致数据入库较慢的情况,此时需要扩大分片的数量,此时可以尝试使用Reindex。
2、当数据的mapping需要修改,但是大量的数据已经导入到索引中了,重新导入数据到新的索引太耗时;但是在ES中,一个字段的mapping在定义并且导入数据之后是不能再修改的,
所以这种情况下也可以考虑尝试使用Reindex。

索引重建示例:
说明:旧的索引主分片数目是4,现在创建新的索引将主分片数目改为8.

PUT my-index-old
{
   "settings": {
        "number_of_shards": "4"
  },
  "mappings": {
    "properties": {
      "company": {
        "type": "text", "analyzer": "ik_smart"
      }
    }
  }
}

PUT my-index-new
{
   "settings": {
        "number_of_shards": "8"
  },
  "mappings": {
    "properties": {
      "company": {
        "type": "text", "analyzer": "ik_smart"
      }
    }
  }
}

通过_reindex索引重建命令导入数据

 POST _reindex?slices=4&refresh
{
  "source": {
    "index": "my-index-old",
    "size": 1000
  },
  "dest": {
    "index": "my-index-new"
  }
}

说明:
1、slices用来控制Scroll遍历的切片,并行化重建索引过程,提高效率。slices的数量等于索引中的分片数量时,
查询性能最高效。
2、size控制批量写入的大小。

reindex请求超时问题
说明:有时候由于索引中的数据量太大会导致reindex请求超时问题。

{
  "statusCode": 504,
  "error": "Gateway Time-out",
  "message": "Client request timeout"
}

解决:
添加wait_for_completion=false参数,不用等待请求结束。

 POST _reindex?slices=4&refresh&wait_for_completion=false
{
  "source": {
    "index": "my-index-old",
    "size": 1000
  },
  "dest": {
    "index": "my-index-new"
  }
}

七、索引数据倾斜问题

1、将数据根据特定维度保存在多个索引中,比如根据订单月份保存在多个索引中。
2、通过调整index.routing_partition_size,让数据在分片中均匀分配。


总结

本文主要通过介绍ES中索引的主分片数目不能修改的问题,展开介绍了一系列的问题和解决方案。
1、在创建索引的时候,需要根据索引的数据量和未来的数据增量,预估好索引的主分片数目。
一般一个分片的数据大小不要超过50G。
2、索引的主分片数只能在创建索引时指定,索引创建后就不能动态修改了。
3、介绍了ES的分片路由算法
4、介绍了ES中Get请求的流程
5、基于分片路由算法和Get请求流程,回答了主分片数目不能修改的原因。核心是会导致根据文档Id计算数据保存的分片Id错误导致查询失败。
6、介绍了通过reindex索引重建来修改索引的主分片数量
7、介绍了如何解决索引膨胀问题
8、介绍了索引数据倾斜的解决方案。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斗者_2013

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值