在 Easysearch(兼容 Elasticsearch)的架构中,索引的主分片数(index.number_of_shards
)一旦创建就无法直接修改。这给实际使用带来挑战:
设得太少,查询/写入瓶颈出现;
设得太多,资源浪费、集群不稳;
想变更结构,却发现配置是“写死”的。
本文将带你深入了解三种常见但本质不同的索引重构方式:split
、shrink
、reindex
,教你如何选择合适方案、安全操作,并解释为什么split + shrink 无法取代 reindex 。
📌 一张图概览三种方式
方法
是否重建索引
可否原名使用
改分片数限制
是否保留数据
是否改结构(mapping/settings)
常见用途
split
✅ 新建索引
❌ 不支持
只能 × 倍数(如 1→2→4)
✅ 是
❌ 否
提升写入并发/读性能
shrink
✅ 新建索引
❌ 不支持
只能 ÷ 因数(如 4→2→1)
✅ 是
❌ 否
合并历史数据分片
reindex
✅ 新建索引
✅ 支持(先删)
任意
✅ 是
✅ 支持
自定义结构/分片/升级
🔧 一、split:将分片数量倍增(如 1 → 2 → 4)
适用于: 提升并发能力、增加查询/写入并行度。
✅ 条件要求:
原始索引必须设置 index.blocks.write: true
(只读);主要是防止写入继续增长。
新分片数必须是原主分片的 倍数 ;
不能使用原名,目标索引必须另起新名。
🛠 操作示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 PUT /abc/_settings { "settings" : { "index.blocks.write" : true } } 如果不设置为只读的话,就报错: ```json { "error" : { "root_cause" : [ { "type" : "illegal_state_exception" , "reason" : "index abc must be read-only to resize index. use \"index.blocks.write=true\"" } ], "type" : "illegal_state_exception" , "reason" : "index abc must be read-only to resize index. use \"index.blocks.write=true\"" }, "status" : 500 }
拆分索引(1 → 4) 1 2 3 4 5 6 POST /abc/_split/abc_split_2shards { "settings": { "index.number_of_shards": 2 } }
执行结果如下:
1 2 3 4 5 { "acknowledged": true, "shards_acknowledged": true, "index": "abc_split_2shards" }
如果不是倍数也会报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "error" : { "root_cause" : [ { "type" : "illegal_argument_exception" , "reason" : "the number of source shards [13] must be a factor of [25]" } ] , "type" : "illegal_argument_exception" , "reason" : "the number of source shards [13] must be a factor of [25]" } , "status" : 400 }
查看索引的信息:
1 GET /abc_split_2shards/_settings?flat_settings=true
/_settings:Elasticsearch 提供的 API 端点,用于查看索引设置。 ?flat_settings=true:查询参数,使返回结果以扁平化的键值对形式展示(而非嵌套结构)。
可以看到目标的索引也是只读的,这在 Easysearch 里是 ElasticSearch 不一样的地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "abc_split_2shards" : { "settings" : { "index.blocks.write" : "true" , "index.creation_date" : "1750747232004" , "index.number_of_replicas" : "1" , "index.number_of_shards" : "2" , "index.provided_name" : "abc_split_2shards" , "index.resize.source.name" : "abc" , "index.resize.source.uuid" : "3NY_W5B_TzimoEGdoA74cg" , "index.routing.allocation.initial_recovery._id" : null , "index.routing_partition_size" : "1" , "index.uuid" : "e2BQiTRKTlaTS5OE8kmiXw" , "index.version.created" : "1130099" , "index.version.upgraded" : "1130099" } } }
然后使用这个来解锁 write block。
1 2 3 4 5 6 PUT /abc_split_2shards/_settings { "settings": { "index.blocks.write": false } }
如果你不想让目标索引变成只读。也可以在_split 的时候加上 “index.blocks.write”: false。
1 2 3 4 5 6 7 8 POST /abc_split_2sharxds/_split/qwe { "settings" : { "index.blocks.write" : false , "index.number_of_shards" : 26 } }
🔧 二、shrink:将分片数量整除压缩(如 8 → 4 → 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 PUT /source_index/_settings { "settings" : { "index.blocks.write" : true , "index.routing.allocation.require._name" : "node-1" , "index.number_of_replicas" : 0 } } 如果不是只读同样报错: ```json { "error" : { "root_cause" : [ { "type" : "illegal_state_exception" , "reason" : "index test1 must be read-only to resize index. use \"index.blocks.write=true\"" } ], "type" : "illegal_state_exception" , "reason" : "index test1 must be read-only to resize index. use \"index.blocks.write=true\"" }, "status" : 500 }
合并为一个分片 1 2 3 4 5 6 7 POST /source_index/_shrink/source_index_1 { "settings": { "index.blocks.write": false, "index.number_of_shards": 1 } }
解锁 1 2 3 4 5 6 PUT /source_index_1/_settings { "settings": { "index.blocks.write": false } }
🔧 三、reindex:拷贝数据 + 新建结构 + 替换旧索引
适用于: 任意修改分片数、字段结构、settings,或实现“看起来改了原索引”的效果。
✅ 优势:
唯一支持任意分片数修改 ;
可自由重构 mapping、settings;
可支持保留原名 (删除旧索引 + 重新创建);
可带条件、分页、脚本拷贝数据;
是唯一可模拟“修改原索引分片”的方式。
🛠 操作步骤(保留原名但改变结构): 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 PUT /my_index_v2 { "settings" : { "index.number_of_shards" : 5, "index.number_of_replicas" : 1 }, "mappings" : { "properties" : { "user" : { "type" : "keyword" }, "message" : { "type" : "text" } } } } POST /_reindex { "source" : { "index" : "my_index" }, "dest" : { "index" : "my_index_v2" } } DELETE /my_index PUT /my_index { "settings" : { "index.number_of_shards" : 3, "index.number_of_replicas" : 1 }, "mappings" : { "properties" : { "user" : { "type" : "keyword" }, "message" : { "type" : "text" } } } } POST /_reindex { "source" : { "index" : "my_index_v2" }, "dest" : { "index" : "my_index" } } 6. 查看索引,并且删除目标索引 GET _cat/indices/my_index*?v  最后删除my_index_v2 即可 DELETE /my_index_v2
⚠️ 为什么 shrink + split 不能替代 reindex? 很多用户会问:能不能 shrink → split
或 split → shrink
拼接出任意分片数?
答案是:数学上不成立 + 实战限制太多。
操作
说明
split 只能倍增
例如:1 → 2 → 4 → 8 ✅,但不能变成 3、5、6 ❌
shrink 只能整除
例如:8 → 4 → 2 → 1 ✅,但不能变成 3、5 ❌
二者组合
受限于倍数 × 因数关系,不是万能变换 (大多数目标分片数根本到不了)
✅ 唯一万能方式:reindex
可以任意:
调整分片数 ✅
修改字段结构 ✅
改 settings ✅
保留索引名 ✅
✅ 最佳实践总结
场景
推荐方式
理由
写入并发不足(1 → 4)
split
快速、低风险
存储/查询优化(8 → 1)
shrink
节省资源、适合归档
修改索引结构、字段、settings
reindex
最灵活、唯一支持任意结构
想保留原名但改分片数
reindex(配合 delete/recreate)
只有它能实现
不想中断服务
reindex + alias 切换
alias 实现无缝替换
🚀 附加建议 S* split/shrink 一般用于 线上小范围结构调整 ;
reindex 用于 升级、清洗、结构优化 等更大粒度的改造;
如果你不想中断服务,强烈建议使用 alias + reindex 做平滑切换;
不建议用 shrink + split 拼接方案,实际运维性差、数学关系苛刻。E