ElasticSearch主分片和副本分片概念详解

📅 2026/6/25 23:58:54
ElasticSearch主分片和副本分片概念详解
ES 主分片的本质是独立的 Lucene 实例主分片负责数据的写入、修改、删除等操作可以理解成凡是写入操作一定会走主分片。ES中有一个文档(doc)的概念每一个文档只会属于一个主分片.例如一个ES索引有三个主分片分别在A、B、C三个集群节点上当一个user的doc需要写入时那么这个user的doc只会在其中一个主分片上而不是在每个集群节点上都有。主分片数量在ES索引创建时指定当索引创建完成之后则不允许再修改主分片的数量例如下面的案例# 创建一个索引指定主分片数量为3PUT /test01{settings: {number_of_shards: 3}}# 然后再尝试修改其主分片数量PUT /test01/_settings{settings: {number_of_shards: 3}}# 报错如下{error : {root_cause : [{type : illegal_argument_exception,reason : Cant update non dynamic settings [[index.number_of_shards]] for open indices [[test01/FPlsR0XQQba41Fr_2_8m7g]]}],type : illegal_argument_exception,reason : Cant update non dynamic settings [[index.number_of_shards]] for open indices [[test01/FPlsR0XQQba41Fr_2_8m7g]]},status : 400}当我们指定主分片数量时主分片数量并不是越多越好在不考虑机器性能的情况下主分片数量一般是根据其ES集群节点数量及索引存储的数据量来决定有几个参考值单个主分片大小控制在 20GB ~ 50GB 范围内性能最优。主分片数量一般是集群节点的1-3倍。例如3节点的集群一个索引的主分片数量可以为3/6/9。当然这些仅仅只是参考例如一个索引的存储的数据量仅仅只有几个G那么此时只需要给一个主分片即可。为什么呢上文说过 ES 主分片的本质是独立的 Lucene 实例每个分片都要占用独立的系统资源、元数据开销、调度开销数据量小的时候多分片的弊端会被无限放大收益为负具体体现在5个维度1、堆内存资源严重浪费最直观的影响每个 Lucene 实例都要占用固定的堆内存来存储索引元数据段信息、倒排词典、字段缓存、过滤器缓存等写入/查询线程上下文、连接资源分片级别的监控、统计数据。在小分片场景下这些固定开销的占比会极高。例如1GB 索引用 1 个分片元数据开销约 10~30MB占比 1%~3%而1GB 索引用 10 个分片元数据总开销约 100~300MB占比 10%~30%2、查询性能不升反降最影响业务的问题ES 查询的标准流程是协调节点接收请求 → 路由到所有相关分片 → 每个分片独立执行查询 → 协调节点聚合所有分片的结果 → 返回给用户。当主分片越多调度开销聚合开销越大。比如 1GB 索引用 1 个分片只需要执行1次查询无需聚合延迟 10ms 以内比如 1GB 索引用 10 个分片需要执行10次查询还要合并10份结果延迟会变成 30~50ms是单分片的 3~5 倍高并发场景下还会放大问题协调节点要同时处理大量分片级别的请求很容易出现CPU占满、OOM等问题。3、集群元数据管理压力指数级上升影响集群稳定性ES Master 节点需要管理全集群所有分片的元数据分片越多会导致集群状态Cluster State体积越大Master 同步元数据到所有节点的耗时越长创建/删除索引、分片分配、节点上下线等集群操作的执行速度越慢极端情况会导致 Master 卡顿、集群无响应元数据写入持久化的频率越高Master 磁盘IO压力越大。生产经验总分片数超过 1 万的集群稳定性会明显下降很多操作都会变慢。4、写入性能不会提升反而下降很多人误以为「分片越多写入越快」但小数据量场景下完全相反当写入时需要计算路由决定写到哪个分片分片越多路由开销越大。每个分片都要独立执行段合并、刷新磁盘等操作小分片会产生大量小的段文件合并开销翻倍。单分片写入性能本身就可以达到 5000~20000 TPS小数据量日写入低于 100GB完全不需要多分片来扛写入压力。5、运维复杂度大幅提升分片分配、迁移、恢复的成本更高集群扩容、节点故障时大量小分片需要在节点之间移动总耗时比少分片更长。问题排查更难分片越多出现未分配分片、分片损坏的概率越高定位问题更麻烦。快照备份/恢复速度变慢快照需要遍历所有分片的元数据分片越多备份耗时越长。然后ES的读写并行能力是由主分片数量来进行控制的当主分片数量越多那么读写并发支持的越多但是读写不一定更快。这里简单说一下当一台32C/64G 节点NVMe SSD单分片大小 20~30GB支持500-25000左右的QPS这里根据写入的场景来划分、例如写入一个极为复杂的文档且写入的数据量超过10KB那么QPS可能只有500 ~ 3000当写入一个简单的文档例如日志等那么QPS可能为8000-25000左右的QPS主分片存储数据量大小控制上文有说过主分片存储数据量大小推荐为20-50GB那么怎么控制主分片存储的数据量大小呢方式一预估主分片数量在创建索引之前需要预估一下该索引可能会存储多少数据例如该索引会存储100GB数据那么有3个集群节点则这个索引给3个主分片即可方式二使用Rollover ILM自动滚动推荐最适合日志/指标/订单等按时间生成的时序数据ES自动监控分片大小/时间超过阈值自动生成新索引永远不会出现超大分片示例PUT _ilm/policy/log_ilm_policy{policy: {phases: {hot: {actions: {rollover: {max_size: 30GB, // 单分片到30G自动滚动max_age: 7d, // 或者满7天自动滚动哪个条件先到就触发max_docs: 10000000 // 或者满1000万条自动滚动}}},delete: {min_age: 30d, // 超过30天自动删除actions: { delete: {} }}}}}当索引的分片存储数据量太大怎么解决呢方案ASplit拆分推荐速度快不需要全量reindex适用条件分片数只能拆成原分片数的整数倍比如原4分片可以拆成8/12/16个不能拆成6个# 1. 先把索引设为只读PUT /超大索引名/_settings{ index.blocks.write: true }# 2. 拆分原4分片拆成8分片必须是整数倍POST /超大索引名/_split/拆分后的新索引名{settings: {index.number_of_shards: 8}}方案BReindex重建通用任何情况都适用适用条件任意调整分片数没有倍数限制缺点是需要全量同步数据数据量大的话耗时久# 1. 创建新索引设置合适的分片数PUT /新索引名{settings: {number_of_shards: 10, // 改成你需要的分片数number_of_replicas: 1}}# 2. 全量同步数据POST _reindex?wait_for_completiontrue{source: { index: 超大索引名 },dest: { index: 新索引名 }}当索引分片太小碎片太多时怎么解决呢方案AShrink合并推荐速度快不需要全量reindex适用条件分片数只能合并成原分片数的约数比如原8分片可以合并成4/2/1个不能合并成3个限制Shrink 时不能指定 mapping只能沿用源索引 mapping源索引必须所有分片 结构一致、正常 green、只读# 1. 先把索引设为只读并且所有分片移到同一个节点PUT /小分片索引名/_settings{index.blocks.write: true,index.routing.allocation.require._name: 你的节点名称}# 2. 合并原8分片合并成2分片必须是约数POST /小分片索引名/_shrink/合并后的新索引名{settings: {index.number_of_shards: 2}}方案B多个小索引合并成一个大索引适用条件比如把7天的日索引log-2026.04.10 ~ log-2026.04.16合并成一个周索引减少总分片数_reindex 合并多个 mapping 不同的索引时需要下面几点注意1、新索引必须提前创建好统一的 mapping2、所有旧索引的数据都会往这个新 mapping 上靠3、类型冲突时旧数据类型 vs 新 mapping 类型不兼容 → 报错兼容 → 自动转换当旧索引有、新索引没有的字段 → 被丢弃不写入旧索引没有、新索引有的字段 → 写入 null / 默认值// 1. 创建新的周索引设置合适的分片数PUT /log-2026.04.week2{ settings: { number_of_shards: 6 } }// 2. 批量同步所有日索引数据POST _reindex?wait_for_completiontrue{source: { index: [log-2026.04.10,log-2026.04.11,log-2026.04.12,log-2026.04.13,log-2026.04.14,log-2026.04.15,log-2026.04.16] },dest: { index: log-2026.04.week2 }}文档的写入流程ES中文档的写入和主分片有关系我们可以来看下整个文档的写入流程ES文档写入涉及到下面几个组件Client客户端REST/Java APICoordinating Node协调节点接收请求、路由、返回结果Primary Shard主分片写入核心数据强一致Replica Shard副本分片异步 / 同步复制高可用Memory BufferIndex Buffer内存缓冲高速写入不可搜索Translog事务日志断电恢复数据不丢SegmentLucene段文件倒排索引可搜索Disk持久化磁盘其写入流程主要分为7个步骤1、客户端发起请求-ES协调节点写入客户端发送PUT /my_index/_doc/1请求到达任意节点该节点成为 Coordinating Node协调节点2、路由计算 → 确定主分片默认按照_id哈希当用户指定id时会按照id字段哈希然后协调节点查询集群元数据找到该主分片所在节点_id哈希公式shard_num hash(_id) % number_of_primary_shards3、转发请求 → 主分片节点协调节点把请求转发给 主分片所在的数据节点4、主分片写入核心内存 Translog主分片所在节点执行写入 Memory BufferIndex Buffer数据进内存此时不可搜索写入速度极快内存级同步写入 Translog事务日志每条写入都追加到 Translog并刷到磁盘默认 request 模式强一致作用宕机后从 Translog 恢复内存数据防止丢失5、主分片 → 副本分片 同步主分片并行把请求发给其对应的所有副本分片副本分片执行和主分片一样的逻辑写内存 写 Translog副本全部成功后主分片才标记写入成功6、返回结果 → 客户端主分片向协调节点返回成功协调节点向客户端返回 201 Created7、后台异步Refresh Flush7.1 Refresh默认 1 秒近实时搜索触发refresh_interval: 1s 或内存满动作Memory Buffer → 生成 新 Lucene Segment倒排索引Segment 写入 OS Cache文件系统缓存不直接落盘此时文档可被搜索NRT 近实时清空 Memory Buffer7.2 Flush默认 30 分钟持久化触发30 分钟、Translog 达 512MB、手动 flush动作所有内存 Segment → 强制刷到磁盘执行 Lucene Commit生成 commit 点清空 Translog释放空间回到顶部副本分片概述ES中的副本分片是主分片的完全拷贝数据上和主分片完全一致。可以看上面文档的写入流程当文档完全写入主分片和副本分片时才会标记文档写入成功副本分片是实现ES高可用的基础当主分片挂了之后副本分片会自动升级为主分片。此时有一个问题当主分片挂了之后副本分片升级为主分片之后那么还会有主分片吗主分片挂了之后可以理解成当前主分片的节点挂了副本分片升级为主分片之后此时是集群为yellow是没有副本分片的。主要当原主分片所在的节点恢复之后此时整个集群变为green副本分片才会恢复。并且副本分片可以承载ES中读的请求提高ES集群查询的吞吐量。ES中同一份数据的主分片和副本分片永远不会分配在同一个节点上ES内置反亲和规则避免单节点故障同时丢主副例如当前集群有三个节点主分片在A节点上那么副本分片永远不会在A节点上只会在B或者C节点中。副本数可以动态调整随时修改不需要重建索引示例# 创建一个索引指定副本分片为1PUT /test01{settings: {number_of_shards: 3,number_of_replicas: 1}}# 查看一下GET /test01/_settings# 返回结果{test01 : {settings : {index : {routing : {allocation : {include : {_tier_preference : data_content}}},number_of_shards : 3,provided_name : test01,creation_date : 1776394680601,number_of_replicas : 1, # 副本分片数量uuid : FSLjaib0Rv2JXvd_wlg2MQ,version : {created : 7172699}}}}}# 修改副本分片数量PUT /test01/_settings{settings: {number_of_replicas: 2 #修改为2}}# 查看一下GET /test01/_settings{test01 : {settings : {index : {routing : {allocation : {include : {_tier_preference : data_content}}},number_of_shards : 3,provided_name : test01,creation_date : 1776394680601,number_of_replicas : 2, # 主分片为2uuid : FSLjaib0Rv2JXvd_wlg2MQ,version : {created : 7172699}}}}}