这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 本章按故障现象组织,给出确认命令、常见原因、处理步骤和预防动作。事故处理时先止血,再定位,最后复盘;不要在未确认影响面的情况下做大规模重建或重启。
30.1 通用排查流程
确认影响面
-> 保存现场
-> 判断集群/节点/索引/请求/链路
-> 执行止血动作
-> 根因定位
-> 验证恢复
-> 复盘改进
第一时间执行的命令:
GET /_cluster/health?timeout=5s
GET /_cat/nodes?v&h=name,node.role,master,heap.percent,cpu,disk.used_percent
GET /_cat/indices?v&health=red
GET /_cat/shards?v&h=index,shard,prirep,state,store,node,unassigned.reason
GET /_cluster/allocation/explain
GET /_cat/thread_pool/write,search?v&h=node_name,name,active,queue,rejected
保存现场:
GET /_cluster/state?filter_path=metadata,routing_table
GET /_nodes/stats?human
GET /_nodes/hot_threads
GET /_cat/pending_tasks?v
30.2 集群 Yellow
现象:主分片正常,部分副本未分配。
常见原因:
- 新增索引后节点资源不足;
- 节点离线;
- 磁盘 low/high watermark;
- 副本数 >= 数据节点数;
- allocation 被禁用;
- 索引分配过滤规则冲突;
- 节点角色不匹配。
排查:
GET /_cluster/health?level=indices
GET /_cluster/allocation/explain
GET /_cat/allocation?v
GET /_cluster/settings?flat_settings=true
处理:
- 找到具体索引和分片;
- 阅读 allocation explain;
- 如果节点数不足,恢复节点或降低副本数;
- 如果磁盘水位高,清理或扩容;
- 如果 allocation 禁用,确认维护窗口后恢复;
- 如果过滤规则错误,修正 tier 或 attribute。
临时降低副本必须经过明确审批:
PUT logs-app/_settings
{
"number_of_replicas": 0
}
30.3 集群 Red
现象:至少一个主分片不可用。
常见原因:
- 持有主分片的节点离线;
- 分片分配失败;
- 磁盘 flood stage;
- 损坏的分片数据;
- 创建索引时节点资源不足;
- 滚动重启时过早移除节点;
- 网络分区导致节点离开。
排查:
GET /_cat/indices?v&health=red
GET /_cat/shards?v&h=index,shard,prirep,state,store,node,unassigned.reason
GET /_cluster/allocation/explain
处理顺序:
- 确认是否所有节点在线;
- 恢复离线节点;
- 查看是否能从副本提升主分片;
- 查看磁盘水位;
- 查看 allocation enable;
- 必要时从快照恢复索引;
- 确认数据缺失影响面。
红线:
- 不要直接删除索引;
- 不要随便丢弃 stale 主分片;
- 不要在没有快照验证时重建;
- 不要同时重启多个数据节点;
- 不要忽视 red 只处理 yellow。
30.4 Unassigned Shard
定位原因:
GET /_cluster/allocation/explain
输出中的 allocate_explanation、node_allocation_deciders 通常会说明被哪个决策器阻止。
常见 decision:
| 原因 | 处理 |
|---|---|
| disk watermark | 清理或扩容 |
| same shard conflict | 增加数据节点 |
| allocation disabled | 恢复 allocation |
| tier mismatch | 调整 tier preference 或节点属性 |
| no node | 恢复节点 |
| shard failure | 查看节点日志和损坏原因 |
重试分配:
POST /_cluster/reroute?retry_failed=true
这个命令只能重试已失败决策,不能解决磁盘满或节点不足等根因。
30.5 磁盘满
确认:
GET /_cat/allocation?v
GET /_nodes/stats/fs?human
GET /_cat/indices?v&h=index,store.size,pri.store.size
处理顺序:
- 暂停非必要导入和离线查询;
- 删除确认过期的索引;
- 缩短 ILM 保留期;
- 降低日志副本;
- 扩容磁盘或节点;
- 触发 reroute;
- 解除 read-only block。
查看 blocked 索引:
GET /_all/_settings?filter_path=*.settings.index.blocks.read_only_allow_delete
解除:
PUT /_all/_settings
{
"index.blocks.read_only_allow_delete": null
}
只有先释放空间再解除,否则很快再次触发。
30.6 写入 429
现象:
EsRejectedExecutionException: rejected execution on write thread pool
排查:
GET /_cat/thread_pool/write?v&h=node_name,name,active,queue,rejected,completed
GET /_nodes/stats/indices/indexing,merge,refresh?human
GET /_cat/allocation?v
常见原因:
- 客户端并发过高;
- bulk 过小;
- merge 太重;
- 磁盘慢或接近水位;
- 副本恢复占用 IO;
- Mapping 过于复杂;
- 写入与查询争抢资源;
- 分片分布热点。
止血:
- 写入端限流;
- 指数退避;
- 暂停低优先级任务;
- 延长 refresh interval;
- 排查磁盘和节点热点。
客户端重试示例:
long delay = 500L;
for (int attempt = 0; attempt < 5; attempt++) {
BulkResponse response = client.bulk(request);
if (!response.hasFailures()) {
return;
}
// 记录并只重试可重试 item,同时遵守退避
Thread.sleep(delay);
delay *= 2;
}
30.7 写入延迟高
排查路径:
| 检查项 | 命令 |
|---|---|
| 写入统计 | GET index/_stats/indexing |
| 合并压力 | GET index/_stats/merges |
| 刷新耗时 | GET index/_stats/refresh |
| flush 耗时 | GET index/_stats/flush |
| 线程池 | _cat/thread_pool/write |
| 磁盘 | _nodes/stats/fs |
| 分片大小 | _cat/shards |
常见原因:
- 单条写入;
- bulk 太小或太大;
- refresh 频繁;
- update 热点;
- 副本慢;
- translog durability 太强但磁盘慢;
- segment 太多;
- Mapping 嵌套复杂。
优先调整:
- 批量;
- refresh interval;
- 写入并发;
- 热点更新;
- 分片和磁盘容量。
30.8 查询慢
排查:
GET /_nodes/stats/indices/search,thread_pool?human
GET /_cat/thread_pool/search?v
GET products-v3/_stats/search?human
常见原因:
| 原因 | 特征 |
|---|---|
| 查询扇出大 | 涉及分片多,CPU 全节点升高 |
| 时间范围宽 | 日志类最常见 |
| 深分页 | from+size 大 |
| 高基数聚合 | heap 和 CPU 高 |
| wildcard | build_scorer 高 |
| script | collect/score 高 |
| fetch 大 | fetch phase 高 |
| 页缓存不足 | 磁盘 read 高 |
| 分片热点 | 个别节点负载高 |
止血:
- 取消当前搜索任务;
- 限制 Kibana 或分析端访问;
- 降低查询 QPS;
- 关闭明显错误的大查询;
- 必要时让索引 read-only 或隔离流量。
查看任务:
GET /_tasks?actions=*search*&detailed
POST /_tasks/TASK_ID/_cancel
30.9 JVM 高与熔断
确认:
GET /_nodes/stats/jvm,breaker?human
GET /_nodes/hot_threads
常见原因:
- 聚合桶过多;
- 深分页;
- 大 size;
- 大 scroll/PIT;
- fielddata;
- 大 bulk 请求;
- segment 内存高;
- 查询响应 JSON 过大。
止血:
- 取消大查询;
- 限制聚合和分页;
- 关闭相关 Dashboard;
- 必要时滚动重启前先切流;
- 确认恢复后再放流量。
不要只根据 heap 使用率重启。先看 GC 是否能回收、 breaker 类型、当前任务和 hot threads。
30.10 Master 不稳定
现象:
- master 频繁切换;
- cluster state publish timeout;
- pending tasks 堆积;
- 创建索引长时间无响应;
- 节点反复 join/leave。
排查:
GET /_cluster/health?explain=true
GET /_cat/pending_tasks?v
GET /_nodes/hot_threads
GET /_cat/nodes?v&h=name,node.role,master,cpu,load_average,heap.percent
常见原因:
- master 节点资源不足;
- master 与 data 混部被重查询拖慢;
- cluster state 过大;
- 索引和分片数量过多;
- 网络抖动;
- GC 长暂停;
- 3 节点集群同时维护两个节点。
处理:
- 恢复 master-eligible 节点数量;
- 降低 cluster state 规模;
- 清理无用索引和模板;
- 分离 master 节点;
- 排查网络和 GC;
- 控制索引创建频率。
30.11 副本恢复慢
查看:
GET /_cat/recovery?v&active_only=true
GET /_cat/shards?v&h=index,shard,prirep,state,store,node
常见限制:
- recovery 带宽;
- 磁盘 IO;
- 节点 CPU;
- 分片大小;
- translog 重放;
- 并发恢复数。
调整恢复速度要谨慎:
PUT /_cluster/settings
{
"persistent": {
"indices.recovery.max_bytes_per_sec": "100mb"
}
}
恢复期间业务影响:
- 写入延迟上升;
- 查询可能路由到正在恢复的节点;
- 磁盘和网络被占用;
- merge 变慢。
30.12 数据不一致或丢失
先确认问题类型:
| 现象 | 可能原因 |
|---|---|
| 查不到最新数据 | refresh 未发生 |
| 主库有但 ES 没有 | 同步链路断 |
| ES 有重复数据 | 幂等缺失 |
| 字段缺失 | Mapping/dynamic 策略 |
| 数值统计不对 | 指标口径或近似聚合 |
| 索引被删 | 权限或误操作 |
| 节点数据损坏 | 磁盘故障 |
排查顺序:
- 按业务 ID 点查;
- 检查
_source与主库差异; - 查看同步任务 offset;
- 查看 Kafka lag 和死信;
- 检查 bulk item 错误;
- 检查 Mapping 和 pipeline;
- 检查最近变更和审计日志;
- 必要时按 ID 重放。
不要把 ES 当主数据源。恢复顺序应以数据库、消息系统或快照为准。
30.13 滚动重启
滚动重启前:
- 确认集群 green 或明确当前风险;
- 暂停索引分配;
- 确认快照可恢复;
- 记录分片分布;
- 通知写入方准备重试;
- 一次只重启一个节点。
暂停分配:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "primaries"
}
}
节点恢复并加入后,再恢复:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": null
}
}
如果使用 none,必须确保维护过程没有索引创建或节点二次故障。
30.14 事故复盘模板
事故时间:
发现时间:
恢复时间:
影响范围:
业务指标:
时间线:
根因:
contributing factors:
止血动作:
长期改进:
责任人:
截止时间:
验证方式:
复盘重点不是处罚,而是补齐四件事:监控、自动化、权限和演练。
本章小结
故障排查要先判断影响面和资源层级,再使用 health、allocation explain、stats、thread pool、tasks 和 hot threads 定位。集群问题时看分片与水位,写入问题时看 bulk、merge、translog 和磁盘,查询问题时看扇出、分页、聚合、缓存和 fetch。
思考题
- yellow 和 red 的第一步排查有什么不同?
- 磁盘 flood stage 解锁前必须先做什么?
- 为什么 429 出现时不建议先加大线程池队列?
- 如何区分“数据没写入”和“数据写入但不可见”?
- 滚动重启时为什么要控制 allocation?