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

简介: 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请求读取单个文档的流程如下:

91.png

说明:


客户端向Node1发送读请求

Node1使用文档ID来确定文档属于分片0,通过集群状态中的内容路由表信息获知分片0有三个副本数据,

位于所有的三个节点中,此时它可以将请求发送到任意节点,这里它将请求转发到Node2.

Node2将文档返回给Node1,Node1将文档返回给客户端。

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


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


四、问题解答


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


在ES中索引数据写入时,需要根据主分片的数目和文档id来进行路由计算,确定文档保存的分片Id。

在根据文档Id读取单个文档数据时,也需要首先根据主分片的数目和文档id来进行路由计算,确定文档保存的分片Id。

如果在索引创建后,再对索引的主分片数进行修改 ,则会造成后续根据路由算法计算的分片id不准确,导致Get请求获取不到对应数据的问题。


核心:

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


五、索引膨胀问题


索引的主分片数目只能在创建索引时指定,且后续不能对主分片数目进行修改。

那么如果由于前期估算的误差,只给索引配置了的主分片数太少,而后续大量数据持续写入,就会造成

这些分片里面的数据不断增长,甚至出现一个分片几百G的情况。


针对这种索引主分片中数据膨胀的问题,我们该如何解决呢?

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

2、将数据保存到多个索引中,然后给索引指定相同的索引别名。

理论上来说,查询一个有8个分片的索引和2个具有4个分片的索引查询效率是一样的。

90.png



六、通过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、介绍了索引数据倾斜的解决方案。

目录
相关文章
|
API 索引
es实战-分片分配失败解决方案
分片无法分配情况的一些解决办法
2199 0
|
数据处理 数据库
08MyCat - 概念 - 分片节点、分片规则、全局序列号
08MyCat - 概念 - 分片节点、分片规则、全局序列号
61 0
38MyCat - 分片规则(自然月分片)
38MyCat - 分片规则(自然月分片)
68 0
|
存储 NoSQL MongoDB
路由节点进行分片操作|学习笔记
快速学习路由节点进行分片操作
路由节点进行分片操作|学习笔记
|
存储 负载均衡 NoSQL
分片集群使用及原理介绍(一)|学习笔记
快速学习分片集群使用及原理介绍
713 0
分片集群使用及原理介绍(一)|学习笔记
|
存储 负载均衡 NoSQL
分片集群使用及原理介绍(二)|学习笔记
快速学习分片集群使用及原理介绍
313 0
分片集群使用及原理介绍(二)|学习笔记
|
存储 算法 中间件
MyCat - 分片 - 分片规则 - 固定分片 hash 算法 | 学习笔记
快速学习 MyCat - 分片 - 分片规则 - 固定分片 hash 算法
MyCat - 分片 - 分片规则 - 固定分片 hash 算法 | 学习笔记
|
存储 关系型数据库 MySQL
MyCat - 分片 - 分片规则 - 准备工作 | 学习笔记
快速学习 MyCat - 分片 - 分片规则 - 准备工作
MyCat - 分片 - 分片规则 - 准备工作 | 学习笔记
|
算法 关系型数据库 MySQL
MyCat - 分片 - 分片规则 - 枚举分片 | 学习笔记
快速学习 MyCat - 分片 - 分片规则 - 枚举分片
MyCat - 分片 - 分片规则 - 枚举分片 | 学习笔记
|
存储 NoSQL MongoDB
第一个路由节点创建|学习笔记
快速学习第一个路由节点创建
第一个路由节点创建|学习笔记