Elasticsearch 分片和副本:容量怎么规划,改分片数为什么这么麻烦
版本说明
本文基于 Elasticsearch 7.x/8.x,分片数不可变的机制自 ES 诞生起从未改变。
# 1. 分片(shard)和副本(replica)分别解决什么问题
Elasticsearch 是分布式搜索引擎,一个索引的数据不会整体存在一台机器上,而是拆成若干分片分散到不同节点——这是它能水平扩展、并行处理查询的基础:分片数越多,理论上能并行处理的查询并发度越高(但不是越多越好,见下文)。
副本是分片的完整拷贝,解决的是另一个正交的问题:可用性和读吞吐。某个节点挂了,它上面的主分片如果有副本在别的节点上,集群可以直接把副本提升为主分片,数据不丢;同时查询请求可以分摊到主分片和副本上,提高读并发能力。
一句话区分:分片数决定写入和存储的水平扩展能力,副本数决定容错能力和读吞吐,两者独立配置,互不替代。
# 2. 分片数为什么创建后几乎不能改
PUT /my_index/_settings
{
"settings": {
"number_of_shards": 3
}
}
2
3
4
5
6
这条命令实际上会报错——number_of_shards 是索引创建时就固定死的静态设置,_settings API 只能修改动态设置,不能用来改分片数。这是一个常见的误解,根源在于分片的物理机制:每个分片本质上是一个独立的 Lucene 索引实例,文档路由到哪个分片是通过 hash(routing) % number_of_shards 计算的——一旦分片数变了,这个哈希公式的结果全变,所有已经写入的文档理论上都要重新计算归属、重新分布,这就不是一个简单的"改配置"能完成的操作。
真的需要调整分片数,有两条路:
# 方式一:Reindex 到一个新建的、分片数不同的索引
PUT /my_index_v2
{ "settings": { "number_of_shards": 6 } }
POST /_reindex
{
"source": { "index": "my_index" },
"dest": { "index": "my_index_v2" }
}
# 方式二:用 Split/Shrink API(分片数必须是原数量的整数倍/因数关系)
POST /my_index/_split/my_index_v2
{
"settings": { "number_of_shards": 6 }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_reindex 最灵活但需要重新写一遍全部数据,耗时耗资源;_split/_shrink 更轻量,但对分片数的倍数关系有限制(_split 要求新分片数是原分片数的整数倍,_shrink 反过来要求原分片数能被新分片数整除)。这也是为什么"分片数规划"必须在创建索引前就想清楚——事后补救的成本远高于事前规划。
# 3. 副本数则可以随时动态调整
PUT /my_index/_settings
{
"settings": {
"number_of_replicas": 2
}
}
2
3
4
5
6
这条命令是有效的——副本数不影响文档路由算法,只是"多复制几份"或"少复制几份",ES 会在后台自动完成副本的创建或移除,不需要重建索引。
# 4. 容量规划:分片数该设多少
没有万能公式,但有一个业界常用的经验起点:单个分片大小控制在 20GB~50GB 之间。
建议分片数 ≈ 预计索引总数据量 / 单分片目标大小(如 30GB)
比如预计这个索引未来会累积到 300GB 数据,除以 30GB,大致 10 个主分片是合理起点。这个经验值背后的原因:分片太大,单个分片的查询和 merge 开销会显著增加,恢复/迁移这个分片时也更慢;分片太多,则每个分片自带的元数据开销(如 Lucene segment、集群状态里的分片元信息)会累积成不可忽视的额外负担,过多的小分片反而会拖累集群整体性能(俗称"分片过度" oversharding)。
一个常见反例:给一个预计只有几百 MB 数据的小索引设置成 20 个分片。这样做的后果是每个分片只有几十 MB 数据,远低于合理下限,集群要维护 20 份分片的元数据和 Lucene 实例开销,纯粹是浪费——小索引应该只给 1 个主分片(ES 7.0+ 的默认值已经改成了 1,早期版本默认是 5,这也是很多旧集群"分片过度"问题的历史根源)。
# 5. 坑
- 修改分片和副本数量都可能触发数据重新分配/迁移(副本数增加时需要复制数据,分片重建需要 reindex),这个过程会占用集群 I/O 和网络资源,生产环境操作应安排在非高峰期。
- 副本数不是越多越安全——每增加一份副本,就多一份存储和写入开销(写入需要同步到所有副本才算成功),单纯堆副本数不是免费的高可用。
- 规划分片数时要结合节点数量:单个节点如果承载了同一索引的主分片和它的副本,节点故障时两份数据同时不可用,规划时要确认集群的分片分配策略(
allocation.awareness等)能让主副分片分散到不同节点/可用区。
# 6. 可复用要点
- 分片数创建后基本不可变,规划要"事前想清楚"而不是"事后再调";副本数可以随时动态调整。
- 单分片大小经验值控制在 20GB~50GB,用预计数据总量倒推分片数,避免分片过大或过度分片。
- 小索引不要给过多分片——分片数量本身有固定的元数据开销,过度分片是常见的性能反模式。
- 01
- Nginx 运维知识地图:从配置基础到反向代理实战 原创07-29
- 02
- MySQL 运维知识地图:从入门配置到高可用排障 原创07-29