bigywater.tech
Elasticsearch · 146

Circuit Breaker 类型

本机真实查_nodes/stats/breaker发现6种断路器同时存在,parent断路器的estimated_size真实独立统计出518.8mb而非其他子断路器的简单加总;把request断路器限制压到1kb后跑一个3万桶的聚合真实精确触发circuit_breaking_exception,报错写出想要分配6144字节但限制只有1024字节;查tripped计数器确认记录了这次触发,恢复默认限制后同样的聚合立刻重新成功执行。

查breaker stats
真实同时存在6种断路器,parent单独有518.8mb占用
request断路器限制压到1kb后跑聚合
真实精确触发circuit_breaking_exception
恢复默认限制后重新执行
同样的聚合真实立刻成功跑出全部3万桶

做的事:本机真实查一次_nodes/stats/breaker,看这台节点上真实同时存在几种断路器

代码GET /_nodes/stats/breaker

验证:真实看到6种断路器同时存在,其中parent这一项哪怕其他几个断路器的估算值都是0,自己的estimated_size依然有518.8mb

实测 · 本机 Elasticsearch 8.15.0 执行输出
request           limit=614.3mb  estimated=0b      tripped=0
inflight_requests limit=1gb      estimated=0b      tripped=0
model_inference   limit=512mb    estimated=0b      tripped=0
eql_sequence      limit=512mb    estimated=0b      tripped=0
fielddata         limit=409.5mb  estimated=0b      tripped=0
parent            limit=972.7mb  estimated=518.8mb tripped=0   ← 真实唯独这一项有真实占用

这组真实数据揭示了parent断路器真实不是简单地把其他几个子断路器的占用加总起来——本次实测其他几个子断路器全部显示0占用,但parent真实依然统计出了518.8mb,说明它真实衡量的是一个更大范围的口径(真实接近整个JVM堆的实际使用情况),而不只是"request+fielddata+..."这几个具体场景各自申请的内存总和。

真实把request断路器的限制临时调到1kb这种离谱的小数值,再跑一个会申请较大内存的3万个桶的聚合,看会不会真实被这道断路器拦下来

代码PUT /_cluster/settings {"persistent":{"indices.breaker.request.limit":"1kb"}} GET /cb_test/_search {"size":0,"aggs":{"by_cat":{"terms":{"field":"cat.keyword","size":30000}}}}

验证:真实精确触发了circuit_breaking_exception,报错原文直接写出了想要分配6144字节但限制只有1024字节

实测 · 本机执行输出
→ 429 circuit_breaking_exception:
  "[request] Data too large, data for [preallocate[aggregations]]
   would be [6144/6kb], which is larger than the limit of [1024/1kb]"
  bytes_wanted: 6144, bytes_limit: 1024, durability: "TRANSIENT"
这条真实报错把断路器的工作方式讲得很直白:它真实不是等内存实际爆掉之后才补救,而是真实在即将执行某个内存分配动作之前,先估算这次分配大概需要多少字节(本次是聚合阶段准备申请6144字节),拿这个预估值跟当前配置的限制比一比,一旦超过就真实提前拒绝这次操作,宁可牺牲这一次请求的成功率,也要真实保住整个JVM不被这类内存申请拖垮。durability:"TRANSIENT"这个字段也真实说明了这类因为断路器被拒绝的请求,本质上是"这次不行,换个更小的请求或者等内存压力降下去再试",不是一个需要运维介入修复的持久性故障。

真实查breaker stats确认tripped计数器是否真实记录了这次触发,再把限制恢复默认,确认同样的聚合能不能重新跑通

代码GET /_nodes/stats/breaker PUT /_cluster/settings {"persistent":{"indices.breaker.request.limit":null}} GET /cb_test/_search {同样的3万桶聚合}

验证:tripped计数器真实从0变成了1,恢复默认限制之后同样的聚合真实立刻成功跑出了全部3万个桶

实测 · 本机执行输出
触发后: request断路器 tripped: 1   ← 真实精确记录了这次触发
恢复默认限制后重新执行聚合: → 真实成功,buckets数量: 30000

这组真实数据完整闭环证明了断路器的触发真实是一次性的、针对单次请求的拦截,不会把整个线程池或者索引卡死——只要真实把配置恢复到合理范围,被拦下来的那类操作立刻就能重新正常执行,tripped这个累计计数器则真实留下了"这个断路器曾经真实发挥过作用"的证据,方便事后排查这台节点是不是经历过内存压力过大的场景。

收尾:真实清理测试索引

代码DELETE /cb_test

分步演示:从6种断路器的真实并存状态,到request断路器精确触发拦截,再到恢复默认后的正常重新执行

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

parent断路器统计的是整体内存口径,request断路器真实能精确拦截超额的内存分配
这一步新验证的断路器场景

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 _nodes/stats/breaker API 与 indices.breaker.request.limit 设置
    京ICP备2026067502号-1京公网安备11010502063487号