ES101系列09 | 运维、监控与性能优化

本篇文章主要讲解 ElasticSearch 中 DevOps 与性能优化的内容,包括集群部署最佳实践、容量规划、读写性能优化和缓存、熔断器等。

集群部署最佳实践

在生产环境中建议设置单一角色的节点。

  • Dedicated master eligible nodes:负责集群状态的管理。使用低配置的 CPU,RAM 和磁盘。
  • Dedicated data nodes:负责数据存储及处理客户端请求使用高配置的 CPU,RAM 和磁盘。
  • Dedicated ingest nodes:负责数据处理使用高配置 CPU,中等配置的 RAM,低配置的磁盘。

从高可用 & 避免脑裂的角度出发,一般生产环境需要配置 3 台 Delicate Master Node。

拓展方式
  • 当磁盘容量无法满足需求时,可以增加数据节点;磁盘读写压力大时,增加数据节点。
  • 当系统中有大量的复杂查询及聚合时候,增加 Coordinating 节点,增加查询的性能。

Hot & Warm Architecture

  • Hot & Warm Architecture
    • 数据通常不会有 Update 操作;适用于 Time based 索引数据(生命周期管理),同时数据量比较大的场景。
    • 引入 Warm 节点,低配置大容量的机器存放老数据,以降低部署成本。
  • 两类数据节点,不同的硬件配置
    • Hot 节点(通常使用 SSD):索引有不断有新文档写入。通常使用 SSD。
    • Warm 节点(通常使用 HDD):索引不存在新数据的写入;同时也不存在大量的数据查询。

分片设计

单个分片
  • 7.0 开始,新创建一个索引时,默认只有一个主分片。
    • 单个分片,查询算分,聚合不准的问题都可以得以避免。
  • 单个索引,单个分片时候,集群无法实现水平扩展。
    • 即使增加新的节点,无法实现水平扩展。
两个分片

新增节点后,ElasticSearch 会自动进行分片的移动(Shard Rebalancing)。也就实现了水平拓展。

如何设计分片数
  • 当分片数>节点数时
    • 一旦集群中有新的数据节点加入,分片就可以自动进行分配。
    • 分片在重新分配时,系统不会有 downtime。
  • 多分片的好处:一索引如果分布在不同的节点,多个节点可以并行执行
    • 查询可以并行执行。
    • 数据写入可以分散到多个机器。
如何确定主分片数
  • 从存储的物理角度看
    • 日志类应用,单个分片不要大于 50GB
    • 搜索类应用,单个分片不要超过 20GB
  • 为什么要控制分片存储大小
    • 提高 Update 的性能
    • Merge 时,减少所需的资源
    • 丢失节点后,具备更快的恢复速度/便于分片在集群内 Rebalancing
如何确定副本分片数
  • 副本是主分片的拷贝
    • 提高系统可用性:相应查询请求,防止数据丢失。
    • 需要占用和主分片一样的资源。
  • 对性能的影响
    • 副本会降低数据的索引速度:有几份副本就会有几倍的 CPU 资源消耗在索引上。
    • 会减缓对主分片的查询压力,但是会消耗同样的内存资源。
      • 如果机器资源充分,提高副本数,可以提高整体的查询 QPS。

集群容量规划

  • 一个集群总共需要多少个节点?一个索引需要设置几个分片?
    • 规划上需要保持一定的余量,当负载出现波动,节点出现丢失时,还能正常运行。
  • 做容量规划时,一些需要考虑的因素
    • 机器的软硬件配置。
    • 单条文档的尺寸 / 文档的总数据量 / 索引的总数据量(Time base 数据保留的时间)/ 副本分片数。
    • 文档是如何写入的(Bulk 的尺寸)。
    • 文档的复杂度,文档是如何进行读取的(怎么样的查询和聚合)。
业务性能评估
  • 数据呑吐及性能需求
    • 数据写入的吞吐量,每秒要求写入多少数据?
    • 查询的吞吐量?
    • 单条查询可接受的最大返回时间?
  • 了解你的数据
    • 数据的格式和数据的 Mapping。
    • 实际的查询和聚合长的是什么样的。
硬件配置
  • 数据节点尽量用 SSD。
  • 日志类或并发查询低的场景可以用机械硬盘。
  • 单节点数据控制在 2TB 以内,最高不超过 5TB。
  • JVM 配置机器内存的一半,不建议超过 32G。
集群扩容
  • 增加 Coordinating / Ingest Node
    • 解决 CPU 和内存开销的问题。
  • 增加数据节点
    • 解决存储的容量的问题。
    • 为避免分片分布不均的问题,要提前监控磁盘空间,提前清理数据或增加节点(70%)。

监控 & 排查 API

类别 API 路径 核心用途 关键返回字段说明 生产环境关注点
集群健康 GET /_cluster/health 集群整体状态概览 status(green/yellow/red), unassigned_shards, active_shards_percent 红色状态立即处理;黄色状态检查未分配分片
节点资源 GET /_nodes/stats 节点级资源监控 heap_used_percent, cpu.percent, disk_free_percent Heap >75% 需扩容;磁盘<20% 需清理或扩容
索引性能 GET /_cat/indices?v 索引存储与文档统计 docs.count, store.size, pri.store.size 分片数合理性;定期清理无用索引
慢查询定位 GET /_search?pretty&q=slow 定位高延迟请求 took 时间(需预设慢日志阈值) 优化 took >1s 的查询/索引设计
线程池积压 GET /_cat/thread_pool?v 线程队列监控 search.queue, write.queue 队列>0 表示资源瓶颈,需扩容或限流
分片分布 GET /_cat/shards?v 分片负载均衡检测 node, state, store 均衡分片分布;避免单节点负载过高
任务监控 GET /_tasks?detailed 长任务跟踪 running_time_in_nanos, description 监控耗时任务(如 reindex),避免阻塞集群
磁盘水位 GET /_cat/allocation?v 节点磁盘使用率 disk.avail, disk.used 预警 disk.avail <30% 的节点
Segment 状态 GET /_cat/segments?v 索引碎片监控 segment.count, size 过多小 segment 增加 Heap 压力;触发 force_merge 需谨慎
JVM 状态 GET /_nodes/stats/jvm 垃圾回收监控 young.collection_count, old.collection_time_in_millis Young GC >2 次/秒或 Old GC >10 秒/次需调优堆大小
关键监控指标 API 详解
  1. 集群健康 (/_cluster/health)

    • status: red 表示主分片缺失(数据丢失风险)
    • unassigned_shards 常见原因:节点离线或分片分配规则冲突 。
  2. 节点资源 (/_nodes/stats)

    • JVM Heap 示例

      json 复制代码
      "jvm": { "mem": { "heap_used_percent": 65 } }

持续>75% 需扩容或优化内存(如减少 fielddata/cache)。

  1. 线程池积压 (/_cat/thread_pool)

    bash 复制代码
    node2 search 8 0 8 0 # search.queue=8 表示队列积压

queue>0 需扩容或优化查询并发度 。

  1. 慢查询阈值配置

    elasticsearch.yml 中设置:

    yaml 复制代码
    index.search.slowlog.threshold.query.warn: 10s
    index.search.slowlog.threshold.query.info: 5s
生产环境排查流程建议
  1. 集群异常 → 用 GET /_cluster/allocation/explain 定位未分配分片原因。
  2. 节点负载高 → 执行 GET /_nodes/hot_threads 抓取热点线程栈。
  3. 查询延迟 → 使用 GET /_search?profile=true 分析执行计划。
  4. 磁盘告警 → 优先清理大型临时索引或扩容冷节点 。
分片没有被分配的一些原因
  • INDEX_CREATE:创建索引导致。在索引的全部分片分配完成之前,会有短暂的 Red,不一定代表有问题。
  • CLUSTER_RECOVER:集群重启阶段会有这个问题。
  • INDEX_REOPEN:Open 一个之前 Close 的索引。
  • DANGLING_INDEX_IMPORTED:一个节点离开集群期间,有索引被删除。这个节点重新返回时,会导致问题。
集群变红的常见问题与解决方法
  • 集群变红,需要检查是否有节点离线。如果有,通常通过重启离线的节点可以解决问题。
  • 由于配置导致的问题,需要修复相关的配置(例如错误的 box_type,错误的副本数)。如果是测试的索引,可以直接删除。
  • 因为磁盘空间限制,分片规则(ShardFiltering)引发的,需要调整规则或者增加节点。

读写性能优化

写性能优化

目标增大写吞吐量,越高越好。

  • 客户端:多线程批量写,通过性能测试确定最佳并发度。
  • 服务端:
    • 使用更好的硬件,观察 CPU / IO Block。
    • 降低 IO 操作,使用 ES 自动生成文档 id。
    • 降低 CPU 和存储开销,减少不必要的分词。
    • 尽可能做到写入和分片的均衡负载,实现水平扩展。

一切优化都基于高质量的数据建模。

Bulk、线程池和队列大小
  • 客户端
    • 单个 bulk 请求体的数据量不要太大,官方建议大约 5-15mb。
    • 写入端的 bulk 请求超时需要足够长,建议 60s 以上。
    • 写入端尽量将数据轮询打到不同节点。
  • 服务器端
    • 索引创建属于计算密集型任务,应该使用固定大小的线程池来配置。来不及处理的放入队列,线程数应该配置成 CPU 核心数 +1,避免过多的上下文切换。
    • 队列大小可以适当增加,不要过大,否则占用的内存会成为 GC 的负担。
读性能优化

尽可能 Denormalize 数据,从而获取最佳的性能。

  • 避免查询的时候使用脚本。
  • 避免使用通配符开头的正则。
  • 控制聚合的数量。
优化分片
  • 避免 Over Sharding。
  • 控制单个分片的大小,查询场景尽量 20GB 以内。
  • 将只读的索引进行 force merge。

段合并优化

ES 和 Lucene 会自动进行 Merge 操作,该操作较重,需要优化降低对系统的影响。

  1. 降低分段产生的数量/频率
  • 可以将 Refresh Interval 调整到分钟级别。
  • 尽量避免文档的更新操作。
  1. 降低最大分段大小,避免较大的分段继续参与 Merge,节省系统资源。
  • Index.merge.policy.max_merged_segment 默认 5GB,操作此大小以后就不再参与后续的合并操作。
Force Merge

当 Index 不再有写入操作的时候,建议对其进行 force merge。能够提升查询速度/减少内存开销。

最终分成几个 segments 比较合适?

  • 越少越好,最好可以 merge 成 1 个,但是 Force Merge 会占用大量的网络、IO 和 CPU。
  • 如果不能在业务高峰期之前做完,就需要考虑增大最终的分段数。包括 Shard 的大小、Index.merge.policy.max_merged_segment 的大小。

缓存与内存

缓存类型 作用域 主要作用 触发条件 失效条件
Node Query Cache (Filter Cache) 节点级 缓存过滤查询(Filter)的结果(文档 ID 位图) filter 上下文查询 索引数据变更、手动清除、段合并、LRU 淘汰
Shard Request Cache 分片级 缓存整个搜索请求的完整结果(如聚合查询) size:0 的请求或显式启用 索引数据变更、手动清除、分片 Refresh、LRU 淘汰
Fielddata Cache 分片级 缓存字段值到文档 ID 的映射(用于排序、聚合) text 字段进行聚合/排序 手动清除、段合并、LRU 淘汰
管理内存的重要性

ElasticSearch 高效运维依赖于内存的合理分配。可用内存一半分配给 JVM,一半留给操作系统缓存索引文件。

内存问题可能会导致 GC 和 OOM 问题。

一些常见的内存问题
  • Segments 个数过多,导致 full GC。集群响应慢但没有特别多是读写,节点在持续 full GC。查看内存发现 segments.memory 占用很大空间。通过 force merge 段合并。
  • Field data cache 过大,导致 full GC。集群响应慢但没有特别多是读写,节点在持续 full GC。查看内存发现 fielddata.memory.size 占用很大空间。将 indices.fielddata.cache.size 设小,重启节点,堆内存恢复正常。
Circuit Breaker
熔断器名称 主要功能 触发条件(默认阈值)
Parent Circuit Breaker 总内存保护,防止所有子熔断器总和超过节点内存 所有子熔断器申请内存总和 > 95% JVM 堆
Fielddata Circuit Breaker 防止加载字段数据(如 text 字段聚合)耗尽堆内存 字段数据内存 > 40% JVM 堆
Request Circuit Breaker 限制单个请求(查询、聚合等)的内存使用,防止大请求压垮节点 单个请求内存 > 60% 父熔断器剩余内存
In Flight Requests Circuit Breaker 限制传输中请求(未完成)的总内存,保护网络和内存资源 传输中请求总内存 > 100% JVM 堆
Accounting Circuit Breaker 跟踪分片级资源(如 Lucene 段内存),确保资源释放后及时回收 未释放资源 > 100% JVM 堆
Script Compilation Circuit Breaker 限制一定时间窗口内脚本编译次数,防止频繁编译消耗 CPU 15 分钟内编译次数 > 75 (默认)
Disk Usage Circuit Breaker 防止节点磁盘写满导致集群故障(触发只读保护) 磁盘空间 > 低水位线 (默认 85%)

💡 注:阈值可通过配置调整(如 indices.breaker.fielddata.limit),触发后返回 429 (Too Many Requests) 错误。

写在最后

这是该系列的第九篇,主要讲解 ElasticSearch 中 DevOps 与性能优化的内容,包括集群部署最佳实践、容量规划、读写性能优化和缓存、熔断器等。可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。

同时欢迎关注小红书:LanLance。不定时分享职场思考、大厂方法论和后端经验❤️

参考

  1. https://github.com/onebirdrocks/geektime-ELK/
  2. https://www.elastic.co/elasticsearch/
相关推荐
葫芦和十三5 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充12 小时前
1.面向对象设计思想
后端
IT_陈寒12 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗13 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端