Elasticsearch低基数稀疏字段聚合性能异常缓慢问题排查
Elasticsearch 7.2 稀疏字段聚合性能问题排查与解决
问题背景
我们使用Elasticsearch 7.2,索引里两个字段的映射如下:
{ "state": { "type": "long", "store": true, "null_value": 0 }, "csat": { "type": "integer", "store": true } }
- 总文档数约2.2亿条,
state字段存在于所有文档中,仅有5种取值 csat是稀疏字段,同样只有5种取值,但仅部分文档包含该字段- 对
state的avg聚合速度极快,但对csat的avg聚合(甚至terms聚合)速度慢到难以接受
排查过程
- Profile API分析:定位到性能瓶颈集中在
AvgAggregator类,全程只看到从docvalues获取值的操作,初步怀疑是无csat字段的文档在读取docvalues时消耗了大量时间。 - 热点线程追踪:96.5%的CPU被搜索线程占用,调用栈显示核心耗时在稀疏数值型docvalues的
advanceExact操作上:96.5% (482.4ms out of 500ms) cpu usage by thread 'elasticsearch[es7advcl02-14][search][T#8]' 10/10 snapshots sharing following 39 elements app//org.apache.lucene.codecs.lucene80.IndexedDISI.advanceExact(IndexedDISI.java:399) app//org.apache.lucene.codecs.lucene80.Lucene80DocValuesProducer$SparseNumericDocValues.advanceExact(Lucene80DocValuesProducer.java:424) app//org.elasticsearch.index.fielddata.FieldData$DoubleCastedValues.advanceExact(FieldData.java:446) app//org.elasticsearch.index.fielddata.SingletonSortedNumericDoubleValues.advanceExact(SingletonSortedNumericDoubleValues.java:44) app//org.elasticsearch.search.aggregations.metrics.AvgAggregator$1.collect(AvgAggregator.java:83) ...
原因拆解
对于csat这种稀疏字段,Lucene采用稀疏DocValues存储。当聚合遍历2.2亿条文档时,每遇到一个没有csat的文档,都要执行advanceExact操作跳过它——在数据量极大且字段稀疏度高的场景下,这种频繁的跳过操作会吃掉大量CPU资源。
而state字段存在于所有文档中,用的是稠密DocValues存储,遍历过程中不需要额外的跳过逻辑,聚合效率自然高出一大截。
另外,添加exists过滤后性能提升,也侧面验证了这个结论:过滤后只遍历包含csat的文档,避免了对大量无该字段文档的无效跳过操作,CPU开销直接降了下来。
解决方案建议
- 添加
exists过滤查询:在聚合请求里先通过过滤筛选出包含csat的文档,再执行聚合,示例请求如下:{ "query": { "bool": { "filter": { "exists": { "field": "csat" } } } }, "aggs": { "avg_csat": { "avg": { "field": "csat" } } } } - 给
csat设置null_value(业务允许的情况下):把缺失值映射为一个默认值(比如0),这样Lucene会用稠密DocValues存储该字段,避免稀疏存储带来的性能损耗。修改后的映射如下:{ "csat": { "type": "integer", "store": true, "null_value": 0 } }注意:这个变更需要重新索引数据,必须先评估业务上是否可以接受将缺失值视为默认值。
- 预聚合数据:如果业务场景允许,在写入数据时就按业务维度预先计算好聚合结果,存储到单独的索引中,查询时直接读取预计算结果,彻底避免实时聚合的性能开销。
内容的提问来源于stack exchange,提问作者ffff
相关产品推荐
相关产品推荐

