Elasticsearch Circuit Breaker 是什么?为什么聚合和 fielddata 容易触发熔断?
简化版
Circuit Breaker 是 ES 的内存保护机制,用来在操作预计消耗过多内存时提前拒绝请求,避免节点 OOM。大聚合、fielddata、脚本和高基数字段操作都可能触发熔断。
详细版
ES 查询和聚合会使用堆内存。如果某个请求需要构建大量桶、加载 fielddata 或执行内存密集操作,可能威胁节点稳定。Circuit Breaker 会估算内存使用并在超过阈值时抛错。
- Parent breaker 关注总体内存估算。
- Request breaker 关注单次请求内存。
- Fielddata breaker 关注 text 字段 fielddata 加载。
- 熔断是保护机制,不是 bug 本身。
- 优化方向通常是降低聚合桶数量、使用 keyword/doc_values、限制查询范围、调字段建模。
完整版教学
一、为什么 ES 需要内存熔断
ES 是分布式搜索引擎,单个用户请求可能在多个 shard 上展开,并在内存里构建候选结果、聚合桶、排序结构和字段数据。如果不做限制,一个错误聚合就可能把节点堆内存打满,导致 GC 风暴甚至 OOM。Circuit Breaker 的作用是在系统真正崩掉前提前拒绝高风险请求。它牺牲某个请求成功,换取节点整体稳定。
危险请求:
大范围查询 -> 高基数字段 terms 聚合 -> 每个 shard 构建大量 buckets -> 堆内存暴涨
记忆钩子:Circuit Breaker 不是让查询更快,而是防止一个查询把整台节点拖下水。
二、常见 breaker 分别管什么
ES 有多类 breaker。Parent breaker 看整体估算内存,request breaker 看请求级结构,fielddata breaker 看 fielddata 加载。不同版本细节会变化,但思路一致:对危险内存使用做估算和拦截。面试不需要背所有配置名,但要知道熔断来自内存保护,而不是“ES 随机不让查”。
| Breaker | 关注点 | 常见触发 |
|---|---|---|
| Parent | 总体内存估算 | 多类内存叠加 |
| Request | 单请求内存 | 大聚合、大排序 |
| Fielddata | fielddata 加载 | text 字段聚合排序 |
| In-flight | 网络请求内存 | 大批量请求 |
三、为什么 fielddata 特别危险
keyword 字段通常有 doc_values,适合排序和聚合;text 字段面向全文检索,默认不适合直接聚合。如果你在 text 字段上开启 fielddata 做聚合,ES 可能需要把倒排结构转换成便于按文档访问的内存结构,并加载到堆内存。高基数字段一加载,内存压力会非常大。因此面试里常说:聚合用 keyword,不要随便对 text 开 fielddata。
GET logs/_search
{
"size": 0,
"aggs": {
"by_message": { "terms": { "field": "message" } }
}
}
四、带数字理解高基数聚合
假设你对 userId.keyword 做 terms 聚合,时间范围内有 500 万个不同用户。如果每个 bucket 只估算几十字节,整体也可能达到数百 MB,更别说每个 shard 还要先局部聚合再归并。高基数字段聚合不是不能做,但必须限制范围、控制 size、考虑 composite aggregation 或预聚合。熔断提醒你当前请求的内存模型不可接受。
5000000 buckets × 64B ≈ 320MB
多个 shard + 额外结构 + JVM 对象开销后可能更高
五、触发熔断后应该怎么优化
第一步不是盲目调大 breaker 阈值,而是看请求是否合理。可以缩小时间范围,给查询加过滤条件,降低 aggregation size,改用 keyword/doc_values 字段,避免 text fielddata,使用 composite aggregation 分页聚合,或把指标提前预聚合。只有确认请求合理且节点内存确实配置过低时,才考虑调资源和阈值。
GET logs/_search
{
"size": 0,
"query": { "range": { "@timestamp": { "gte": "now-1h" } } },
"aggs": {
"top_users": { "terms": { "field": "userId.keyword", "size": 100 } }
}
}
六、Breaker 和 JVM 堆的关系
ES 很多关键结构在 JVM 堆上,堆太小容易熔断,堆太大又可能带来 GC 问题。Breaker 的估算也不是完美预测,它是保护阈值,不代表到了阈值才会有风险。生产中要结合 JVM heap 使用率、GC、查询类型和节点角色观察。冷热数据节点、协调节点、ingest 节点的压力来源不同,不能只用一个公式看所有节点。
观察指标:
heap used、GC pause、breaker trips、search thread pool、slowlog、aggregation DSL
七、常见误区与追问
- 误区:Circuit Breaker 报错就是 ES 坏了。 它是在保护节点,真正要看请求和字段设计是否合理。
- 误区:把阈值调大就解决问题。 可能只是把熔断变成 OOM,应该先优化查询和 mapping。
- 误区:text 字段聚合没问题。 text 面向分词检索,聚合应使用 keyword 或合适的 doc_values 字段。
- 追问:高基数 terms 聚合怎么处理? 控制范围和 size,考虑 composite aggregation、预聚合或改业务口径。
- 追问:为什么 breaker 是估算? 请求执行前无法精确知道所有内存,只能基于结构和历史做近似保护。
八、加强记忆
Circuit Breaker 记成“节点保险丝”。大聚合、fielddata、高基数桶、超大请求都可能让保险丝跳闸。回答时要先讲它是保护机制,再讲常见触发点,最后给优化路径:改字段、缩范围、降桶数、预聚合,而不是上来就调阈值。