Easysearch 实战指南:修改索引主分片的三种方式(split shrink reindex

在 Easysearch(兼容 Elasticsearch)的架构中,索引的主分片数(index.number_of_shards)一旦创建就无法直接修改。这给实际使用带来挑战:

  • 设得太少,查询/写入瓶颈出现;
  • 设得太多,资源浪费、集群不稳;
  • 想变更结构,却发现配置是“写死”的。

本文将带你深入了解三种常见但本质不同的索引重构方式:splitshrinkreindex,教你如何选择合适方案、安全操作,并解释为什么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
# 强制所有主分片调度到 node-1
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
# 1. 创建临时索引结构(你想要的新结构)
PUT /my_index_v2
{
"settings": {
"index.number_of_shards": 5,
"index.number_of_replicas": 1
},
"mappings": {
"properties": {
"user": { "type": "keyword" },
"message": { "type": "text" }
}
}
}

# 2. 拷贝数据
POST /_reindex
{
"source": { "index": "my_index" },
"dest": { "index": "my_index_v2" }
}

# 3. 删除旧索引(谨慎)
DELETE /my_index

# 4. 创建同名索引(新结构)
PUT /my_index
{
"settings": {
"index.number_of_shards": 3,
"index.number_of_replicas": 1
},
"mappings": {
"properties": {
"user": { "type": "keyword" },
"message": { "type": "text" }
}
}
}

# 5. 再次拷贝数据(回填)
POST /_reindex
{
"source": { "index": "my_index_v2" },
"dest": { "index": "my_index" }
}

6. 查看索引,并且删除目标索引
GET _cat/indices/my_index*?v

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/64f99ab7b3ba4b4a8e18d53d4e32fab5.png)
最后删除my_index_v2 即可

DELETE /my_index_v2

⚠️ 为什么 shrink + split 不能替代 reindex?

很多用户会问:能不能 shrink → splitsplit → 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

Easysearch 实战指南:修改索引主分片的三种方式(split shrink reindex

https://xu-hardy.github.io/easysearch-实战指南:修改索引主分片的三种方式(split-shrink-reindex/

作者

Xu

发布于

2025-07-02

更新于

2025-07-01

许可协议

评论