Elasticsearch指南 修改索引主分片的三种方式(split shrink reindex)

忘机山人

发布于257天前
博客图片修整中,看不了可以先搜索公众号“忘机山人”看。

https://appstore.lazycat.cloud/#/shop/detail/xu.deploy.elasticsearch


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`(只读);主要是防止写入继续增长。
- 新分片数必须是原主分片的 **倍数**;
- 不能使用原名,目标索引必须另起新名。

### 🛠 操作示例:

````bash
# 设置只读
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)

```
POST /abc/_split/abc_split_2shards
{
  "settings": {
    "index.number_of_shards": 2
  }
}
```

执行结果如下:

```
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "abc_split_2shards"
}
```

如果不是倍数也会报错:

```json
{
  "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
}
```

查看索引的信息:

```
GET /abc_split_2shards/_settings?flat_settings=true
```

> /\_settings:Elasticsearch 提供的 API 端点,用于查看索引设置。
> ?flat_settings=true:查询参数,使返回结果以扁平化的键值对形式展示(而非嵌套结构)。

可以看到目标的索引也是只读的,这在 Easysearch 里是 ElasticSearch 不一样的地方。

```json
{
  "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。

```
PUT /abc_split_2shards/_settings
{
  "settings": {
    "index.blocks.write": false
  }
}
```

如果你不想让目标索引变成只读。也可以在\_split 的时候加上 "index.blocks.write": false。

```json
POST /abc_split_2sharxds/_split/qwe
{
  "settings": {
    "index.blocks.write": false,
    "index.number_of_shards": 26
  }
}

```

## 🔧 二、shrink:将分片数量整除压缩(如 8 → 4 → 1)

> **适用于:** 历史归档数据压缩、节省内存、提升查询效率。

### ✅ 条件要求:

- 所有主分片必须集中在同一节点;
- 原索引必须只读;
- 新分片数必须是旧分片数的 **因数**;
- 同样不能保留原名,需新建索引名。

### 🛠 操作示例:

````bash
# 强制所有主分片调度到 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
}
````

### 合并为一个分片

```
POST /source_index/_shrink/source_index_1
{
  "settings": {
      "index.blocks.write": false,
    "index.number_of_shards": 1
  }
}
```

### 解锁

```
PUT /source_index_1/_settings
{
  "settings": {
    "index.blocks.write": false
  }
}
```

## 🔧 三、reindex:拷贝数据 + 新建结构 + 替换旧索引

> **适用于:** 任意修改分片数、字段结构、settings,或实现“看起来改了原索引”的效果。

### ✅ 优势:

- 唯一支持**任意分片数修改**;
- 可自由重构 mapping、settings;
- 可支持**保留原名**(删除旧索引 + 重新创建);
- 可带条件、分页、脚本拷贝数据;
- 是唯一可模拟“修改原索引分片”的方式。

### 🛠 操作步骤(保留原名但改变结构):

```bash
# 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 → 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 拼接方案,实际运维性差、数学关系苛刻。



![image.png](https://dl.playground.lazycat.cloud/guidelines/459/fcd9d1d8-d3f0-46c6-8bd7-32696a96eee3.png "image.png")

评论

0

暂无评论

说点什么呢~
收藏
0
0
0