灯下哥谭 灯下哥谭
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

灯下哥谭

灯还亮着
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL

  • Redis

  • 高性能KV

  • TiDB

  • Elasticsearch

    • Elasticsearch 运维知识地图:从安装配置到排障加速恢复
    • Elasticsearch 集群安装:RPM 裸机生产部署 vs docker-compose 快速起集群
    • 给Elasticsearch集群添加用户密码
    • Elasticsearch 分片和副本:容量怎么规划,改分片数为什么这么麻烦
      • 1. 分片(shard)和副本(replica)分别解决什么问题
      • 2. 分片数为什么创建后几乎不能改
      • 3. 副本数则可以随时动态调整
      • 4. 容量规划:分片数该设多少
      • 5. 坑
      • 6. 单节点分片上限:踩到 cluster.maxshardsper_node 怎么办
        • 6.1 增加单节点分片限制
        • 6.2 收敛分片数:新索引、模板、以及已有索引的 Shrink
        • 6.3 增加节点分散分片负载
        • 6.4 删除不必要的索引
      • 7. 验证
      • 8. 可复用要点
      • Agent 可直接解析的元数据块
    • Elasticsearch集群节点磁盘使用分配不均解决办法
    • Elasticsearch 索引模板与映射:Composable 模板、mapping 与动态模板
    • Elasticsearch 分页查询三种方案:from/size、search_after、scroll 怎么选
    • Elasticsearch字符串搜索方式
    • Elasticsearch使用wildcard字段模糊匹配
    • Elasticsearch 数据迁移方案对比:esm vs Logstash
    • Nginx Mirror 模块实现三套ES写入网关
    • ES排障两件套:慢查询日志阈值配置 + tcpdump 抓包看真实请求
    • ES 集群恢复太慢?三个参数加速节点/分片恢复
    • Elasticsearch 常用 DSL 语句(速查表)
    • ES 集群 Yellow 复盘:1023 个副本永远分配不出去,问题不在磁盘
  • 数据管道

  • 其他数据库

  • 数据库
  • Elasticsearch
灯下哥谭
2022-03-17
目录

Elasticsearch 分片和副本:容量怎么规划,改分片数为什么这么麻烦

版本说明

本文基于 Elasticsearch 7.x/8.x,分片数不可变的机制自 ES 诞生起从未改变。经 2026-09 复核,语法与默认参数仍适用当前版本。

# 1. 分片(shard)和副本(replica)分别解决什么问题

Elasticsearch 是分布式搜索引擎,一个索引的数据不会整体存在一台机器上,而是拆成若干分片分散到不同节点——这是它能水平扩展、并行处理查询的基础:分片数越多,理论上能并行处理的查询并发度越高(但不是越多越好,见下文)。

副本是分片的完整拷贝,解决的是另一个正交的问题:可用性和读吞吐。某个节点挂了,它上面的主分片如果有副本在别的节点上,集群可以直接把副本提升为主分片,数据不丢;同时查询请求可以分摊到主分片和副本上,提高读并发能力。

一句话区分:分片数决定写入和存储的水平扩展能力,副本数决定容错能力和读吞吐,两者独立配置,互不替代。


# 2. 分片数为什么创建后几乎不能改

PUT /my_index/_settings
{
  "settings": {
    "number_of_shards": 3
  }
}
1
2
3
4
5
6

这条命令实际上会报错——number_of_shards 是索引创建时就固定死的静态设置,_settings API 只能修改动态设置,不能用来改分片数。这是一个常见的误解,根源在于分片的物理机制:每个分片本质上是一个独立的 Lucene 索引实例,文档路由到哪个分片是通过 hash(routing) % number_of_shards 计算的——一旦分片数变了,这个哈希公式的结果全变,所有已经写入的文档理论上都要重新计算归属、重新分布,这就不是一个简单的"改配置"能完成的操作。

真的需要调整分片数,有两条路:

# 方式一:Reindex 到一个新建的、分片数不同的索引(大数据量建议加 slices 并行)
PUT /my_index_v2
{ "settings": { "number_of_shards": 6 } }

POST /_reindex?slices=auto&wait_for_completion=false
{
  "source": { "index": "my_index", "size": 5000 },
  "dest": { "index": "my_index_v2" }
}

# 方式二:用 Split/Shrink API(需先设置索引只读、副本为 0)
# Split:分片数必须是原数量的整数倍
PUT /my_index/_settings
{ "index.blocks.write": true, "index.number_of_replicas": 0 }

POST /my_index/_split/my_index_v2
{
  "settings": { "index.number_of_shards": 6 }
}

# Shrink:分片数必须能被原数量整除
PUT /my_index/_settings
{ "index.blocks.write": true, "index.number_of_replicas": 0 }

POST /my_index/_shrink/my_index_v2
{
  "settings": { "index.number_of_shards": 1 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

_reindex 最灵活但需要重新写一遍全部数据,耗时耗资源;_split/_shrink 更轻量,但对分片数的倍数关系有限制(_split 要求新分片数是原分片数的整数倍,_shrink 反过来要求原分片数能被新分片数整除)。这也是为什么"分片数规划"必须在创建索引前就想清楚——事后补救的成本远高于事前规划。


# 3. 副本数则可以随时动态调整

PUT /my_index/_settings
{
  "settings": {
    "number_of_replicas": 2
  }
}
1
2
3
4
5
6

这条命令是有效的——副本数不影响文档路由算法,只是"多复制几份"或"少复制几份",ES 会在后台自动完成副本的创建或移除,不需要重建索引。


# 4. 容量规划:分片数该设多少

没有万能公式,但有一个业界常用的经验起点:单个分片大小落在 10–50 GB 区间——检索延迟敏感的场景偏 10–20 GB,写多读少的日志/时序场景可以到 30–50 GB。

建议分片数 ≈ 预计索引总数据量 / 单分片目标大小(如 30GB)
1

比如预计这个索引未来会累积到 300GB 数据,除以 30GB,大致 10 个主分片是合理起点。这个经验值背后的原因:分片太大,单个分片的查询和 merge 开销会显著增加,恢复/迁移这个分片时也更慢;分片太多,则每个分片自带的元数据开销(如 Lucene segment、集群状态里的分片元信息)会累积成不可忽视的额外负担,过多的小分片反而会拖累集群整体性能(俗称"分片过度" oversharding)。

一个常见反例:给一个预计只有几百 MB 数据的小索引设置成 20 个分片。这样做的后果是每个分片只有几十 MB 数据,远低于合理下限,集群要维护 20 份分片的元数据和 Lucene 实例开销,纯粹是浪费——小索引应该只给 1 个主分片(ES 7.0+ 的默认值已经改成了 1,早期版本默认是 5,这也是很多旧集群"分片过度"问题的历史根源)。


# 5. 坑

  • 修改分片和副本数量都可能触发数据重新分配/迁移(副本数增加时需要复制数据,分片重建需要 reindex),这个过程会占用集群 I/O 和网络资源,生产环境操作应安排在非高峰期。
  • 副本数不是越多越安全——每增加一份副本,就多一份存储和写入开销(写入需要同步到所有副本才算成功),单纯堆副本数不是免费的高可用。
  • 规划分片数时要结合节点数量:单个节点如果承载了同一索引的主分片和它的副本,节点故障时两份数据同时不可用,规划时要确认集群的分片分配策略(allocation.awareness 等)能让主副分片分散到不同节点/可用区。
  • Split/Shrink 前置条件:必须先设置 index.blocks.write: true(只读)且 number_of_replicas: 0,否则 API 报错;操作完成后记得恢复副本数。
  • Reindex 注意事项:slices=auto 按源索引分片数并行执行;wait_for_completion=false 返回 task ID,适合大数据量场景防止超时;size 控制每批次提取文档数,默认值 1000,大数据量可调大。

# 6. 单节点分片上限:踩到 cluster.max_shards_per_node 怎么办

Elasticsearch 默认的单节点分片上限为 1000,当达到这个上限时,创建新索引的请求会被直接拒绝,返回 validation_exception 错误,报文形如:

this action would add [N] total shards, but this cluster currently has [M]/[L] maximum shards open
1

索引根本不会被创建,因此不会产生未分配分片,集群状态也不会因此变黄(存量索引照常运行)。

怎么确认自己踩的是这个坑:看到上述 validation_exception 错误信息,且 /_cluster/settings?include_defaults=true 返回的 max_shards_per_node 已被撑满。

# 6.1 增加单节点分片限制

修改 cluster.max_shards_per_node,适合临时或紧急情况:

# 使用 REST API 修改(持久化到集群状态)
curl -XPUT -H "Content-Type: application/json" \
  http://localhost:9200/_cluster/settings \
  -d '{
    "persistent": {
      "cluster.max_shards_per_node": 2000
    }
  }'

# 验证设置是否生效
curl -XGET http://localhost:9200/_cluster/settings
1
2
3
4
5
6
7
8
9
10
11

⚠️ 注意:过多的分片可能导致内存和性能问题,这只是权宜之计,不应作为长期方案。

# 6.2 收敛分片数:新索引、模板、以及已有索引的 Shrink

为新索引设置更少的分片:

curl -XPUT -H "Content-Type: application/json" \
  http://localhost:9200/my_new_index \
  -d '{
    "settings": {
      "index": {
        "number_of_shards": 1,
        "number_of_replicas": 1
      }
    }
  }'
1
2
3
4
5
6
7
8
9
10

修改模板的分片配置(影响后续自动创建的索引):

curl -XPUT -H "Content-Type: application/json" \
  http://localhost:9200/_index_template/my_template \
  -d '{
    "index_patterns": ["logs-*"],
    "template": {
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1
      }
    }
  }'
1
2
3
4
5
6
7
8
9
10
11

使用 _shrink 合并已有索引(需先设置只读、副本为 0):

# 前置条件
PUT /my_index/_settings
{
  "index.blocks.write": true,
  "index.number_of_replicas": 0
}

# 执行 shrink(原分片数需能被新分片数整除)
POST /my_index/_shrink/my_index_shrunk
{
  "settings": { "index.number_of_shards": 1 }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 6.3 增加节点分散分片负载

当分片数量增长到单节点难以承受时,通过增加节点来分散负载:

# 新增节点后,ES 会自动触发分片重新均衡
# 如需手动触发重路由
POST /_cluster/reroute

# 查看集群节点和分片分布
curl -XGET http://localhost:9200/_cat/nodes
curl -XGET http://localhost:9200/_cat/shards?v
1
2
3
4
5
6
7

# 6.4 删除不必要的索引

如果某些旧索引不再需要:

# 列出所有索引,按大小排序
curl -XGET "http://localhost:9200/_cat/indices?v&s=store.size:desc"

# 删除指定索引(不可恢复,谨慎操作)
curl -XDELETE http://localhost:9200/my_old_index
1
2
3
4
5

设置索引生命周期管理 (ILM) 自动清理:

PUT _ilm/policy/logs_cleanup_policy
{
  "policy": {
    "phases": {
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7. 验证

# 查看索引的分片分布和大小
GET _cat/shards?v&h=index,shard,prirep,store,node&s=index

# 查看索引设置(确认 number_of_shards/replicas)
GET my_index/_settings/index.number_of_shards,index.number_of_replicas

# 查看集群分片上限设置
GET _cluster/settings?include_defaults=true&filter_path=**.max_shards_per_node

# 查看 task 进度(reindex 异步执行时)
GET _tasks/task_id

# 查看未分配分片的解释
GET _cluster/allocation/explain
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 8. 可复用要点

  1. 分片数创建后基本不可变,规划要"事前想清楚"而不是"事后再调";副本数可以随时动态调整。
  2. 单分片大小经验值落在 10–50 GB 区间(延迟敏感偏下限、日志类偏上限),用预计数据总量倒推分片数,避免分片过大或过度分片。
  3. 小索引不要给过多分片——分片数量本身有固定的元数据开销,过度分片是常见的性能反模式。
  4. 默认单节点 1000 分片上限是可以调整的,但根本解决之道是合理规划分片数、清理无用数据或扩容节点。

# Agent 可直接解析的元数据块

{
  "runbook": {
    "task": "elasticsearch-shard-replica-management",
    "permalinks": ["/pages/a3098c/"],
    "category": "database/elasticsearch",
    "tags": ["shard", "replica", "capacity-planning", "max_shards_per_node"],
    "es_versions": ["7.x", "8.x"],
    "verified_date": "2026-09"
  }
}
1
2
3
4
5
6
7
8
9
10
#学习笔记#Elasticsearch
上次更新: 9/11/2026

← 给Elasticsearch集群添加用户密码 Elasticsearch集群节点磁盘使用分配不均解决办法→

最近更新
01
DeepSeek Harness 实战 06|学习笔记:插件、工具、技能不在同一个维度上 原创
09-11
02
DeepSeek Harness 实战 05|让两个编码 Agent 共用一份长期记忆 原创
09-09
03
DeepSeek Harness 实战 04|学习笔记:从「已知限制」里读出三处设计张力 原创
09-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式