灯下哥谭 灯下哥谭
首页
关于
  • 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 分片和副本:容量怎么规划,改分片数为什么这么麻烦
    • Elasticsearch集群节点磁盘使用分配不均解决办法
      • 一、先搞清楚 ES 到底按什么分配
      • 二、先取证,再动手
      • 三、水位线:三条线各自真正的含义
      • 四、按成因选处置
        • 处置 A:调水位线——只是买时间
        • 处置 B:重建索引,改分片数——治成因 1
        • 处置 C:手动搬分片——治急症
      • 五、怎么确认处置生效
      • 六、坑与边界
    • 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 集群节点磁盘使用分配不均解决办法

同一个集群里,有的节点磁盘 90%,有的还剩一半——这不是「ES 分配算法坏了」,而是分配的粒度是分片,不是字节。理解这一点,后面所有处置手段才有依据。

版本说明

本文写于 2022-03,2026-08 重写。
水位线参数(cluster.routing.allocation.disk.watermark.*)与 _cat/allocation 的行为基于 ES 7.x / 8.x 官方文档核对;8.x 起 transient 集群设置已废弃,本文按 persistent 口径给命令。

# 一、先搞清楚 ES 到底按什么分配

ES 的分片分配决策由一串 decider 串联完成,与磁盘不均直接相关的是两个:

  • ShardsLimitAllocationDecider:保证同一索引的分片尽量分散到不同节点,它数的是分片个数。
  • DiskThresholdDecider:按节点磁盘水位线放行或拦截分配,它看的是字节。

默认情况下前者主导。于是最常见的成因是:

  1. 分片数与节点数不匹配。索引 number_of_shards 为 3、集群有 5 个节点,那么无论怎么均衡,都有 2 个节点不持有这个索引的主分片。
  2. 分片本身大小悬殊。按时间滚动的索引里,某天流量暴涨,那一天的分片可能是平日的十倍——分片个数均衡了,字节依然不均衡。
  3. 冷热标签或 allocation.require 把分片钉死在少数节点上。

别把第 2 条当成第 1 条治。 分片个数已经均衡却依然磁盘不均,说明问题在分片大小分布,调 number_of_shards 对存量索引无效——它只对新建索引生效。

# 二、先取证,再动手

不要一上来就改水位线。先确认不均衡到底出在哪一层:

# 1. 每个节点用了多少磁盘、持有多少分片
GET _cat/allocation?v&s=disk.percent:desc

# 2. 分片级视图:谁大谁小,落在哪个节点
GET _cat/shards?v&h=index,shard,prirep,store,node&s=store:desc

# 3. 有没有分片处于 UNASSIGNED,以及 ES 自己给的原因
GET _cluster/allocation/explain
1
2
3
4
5
6
7
8

判读方法:

  • _cat/allocation 里 shards 列相近但 disk.percent 差很多 → 成因 2(分片大小悬殊)。
  • shards 列本身就不均 → 成因 1 或 3,接着看 allocation/explain 给出的 decider 结论。

allocation/explain 是这里最有用的一个接口——它会直接告诉你是哪个 decider 拒绝了分配,不用猜。

# 三、水位线:三条线各自真正的含义

这三个参数经常被记错,逐条说清楚(以下为 7.x/8.x 官方语义):

参数 默认 达到后发生什么
...disk.watermark.low 85% 不再把新分片分配到该节点。注意:全新索引的主分片不受这条限制,它拦的是副本与已有索引的分片迁入。
...disk.watermark.high 90% ES 开始把该节点上的分片迁走(relocate away),而不是"往别处写"。
...disk.watermark.flood_stage 95% 给该节点上有分片的那些索引加上 index.blocks.read_only_allow_delete,索引转为只读。

三个常见的记错,正好对应三种误处置:

  • ❌「low 到了就不能写入了」→ 实际是不再分配,写入照常,磁盘会继续涨。以为写入停了而不管,结果一路冲到 flood_stage。
  • ❌「high 到了会写到别的节点」→ 实际是迁移分片,迁移本身要占用磁盘和 IO,短期内会让磁盘更紧张。
  • ❌「flood_stage 会锁住所有索引」→ 只锁该节点上有分片的索引。7.4 起水位恢复后该 block 会自动解除,更早的版本需要手动解。

手动解除(确认磁盘已经降下来之后):

PUT /_all/_settings
{ "index.blocks.read_only_allow_delete": null }
1
2

# 四、按成因选处置

# 处置 A:调水位线——只是买时间

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "80%",
    "cluster.routing.allocation.disk.watermark.high": "85%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%",
    "cluster.routing.allocation.cluster_concurrent_rebalance": "2",
    "cluster.routing.allocation.node_concurrent_recoveries": "2"
  }
}
1
2
3
4
5
6
7
8
9
10

这是止血,不是根治:把 low/high 调低只是让 ES 更早开始躲开满节点,磁盘总量没变。真正的作用是给你争取时间去做 B 或 C。

两个必须知道的代价:

  • 调低 high 会立刻触发分片迁移,迁移期间集群 IO 和网络会明显上升,要挑低峰做。
  • cluster_concurrent_rebalance 调大能加快均衡,但同样吃 IO,2 是个保守起点。

不要用 transient。 老文档里常见 "transient",但它在集群完全重启后丢失,而且 ES 8.x 已将其标记为废弃。persistent 才是对的。

# 处置 B:重建索引,改分片数——治成因 1

number_of_shards 不能原地修改,只能重建:

POST _reindex?slices=auto&wait_for_completion=false
{
  "source": { "index": "my_index", "size": 5000 },
  "dest":   { "index": "my_index_new" }
}
1
2
3
4
5

分片数怎么定:让单个分片落在 10–50 GB 区间(取值口径与分片和副本一致:延迟敏感偏 10–20 GB,日志类偏 30–50 GB),而不是机械地"等于节点数"。分片开太多,每个分片都有独立的 Lucene 段和内存开销,几百个小分片对 master 和堆内存都是负担。

跑完之后用别名切换,避免改客户端:

POST _aliases
{
  "actions": [
    { "remove": { "index": "my_index",     "alias": "my_index_read" } },
    { "add":    { "index": "my_index_new", "alias": "my_index_read" } }
  ]
}
1
2
3
4
5
6
7

坑:

  • slices=auto 按源索引分片数切分,源索引分片少的时候并行度上不去,别指望它总能跑满。
  • reindex 期间源索引仍在写入的话,新索引会缺掉这段增量——要么先停写,要么补一轮按时间范围的增量 reindex。
  • reindex 期间磁盘占用是翻倍的:新旧索引同时存在。磁盘已经 90% 的时候直接 reindex 会把集群推进 flood_stage,这时应该先做处置 A 或先删旧数据。

# 处置 C:手动搬分片——治急症

某个节点马上要满,等不及自动均衡:

POST _cluster/reroute
{
  "commands": [{
    "move": {
      "index": "my_index", "shard": 3,
      "from_node": "node-hot", "to_node": "node-idle"
    }
  }]
}
1
2
3
4
5
6
7
8
9

只在应急时用。手动 reroute 与自动均衡是会互相拉扯的——搬完记得确认 ES 没有又把它搬回去。

# 五、怎么确认处置生效

每一步都要能验证,否则等于没做:

# 磁盘占比是否收敛(关注 max 与 min 的差值,而不是单个节点)
GET _cat/allocation?v&s=disk.percent:desc

# 是否还有分片在迁移
GET _cat/recovery?v&active_only=true

# 集群是否回到 green,有没有未分配分片
GET _cluster/health?level=indices
1
2
3
4
5
6
7
8

预期:disk.percent 的极差持续收窄、_cat/recovery 的 active 记录逐步清空、_cluster/health 为 green 且 unassigned_shards 为 0。

# 六、坑与边界

  • 本文的处置都假设集群还有可写节点。 如果所有节点都过了 high 水位,迁移无处可去,_cluster/reroute 也会被 decider 拒绝——那时只有扩容或删数据两条路,任何参数调整都不解决问题。
  • _cat/allocation 的 disk.avail 是文件系统视角,如果数据目录和系统盘同一个分区,别的进程写日志一样会把水位推上去,别只盯着 ES。
  • 调低 flood_stage 是危险动作:这条线是最后的数据保护,把它往上调(比如 98%)等于把「只读保护」推迟到磁盘几乎写满,届时 Lucene 段合并可能因空间不足失败,代价远大于只读。
  • 分片大小悬殊的根因常在写入侧(按天滚动 + 流量尖峰)。长期解法是按大小滚动(ILM 的 max_primary_shard_size)而不是按天,否则处置完下个月还会复发。

生产环境执行顺序

先 _cat/allocation 取证 → 再 allocation/explain 定成因 → 低峰期做处置 → 每步都用第五节的命令验证。跳过取证直接改水位线,是这类故障里最常见的返工原因。

#故障复盘#Elasticsearch
上次更新: 9/11/2026

← Elasticsearch 分片和副本:容量怎么规划,改分片数为什么这么麻烦 Elasticsearch 索引模板与映射:Composable 模板、mapping 与动态模板→

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