目录
[1. 概述](#1. 概述)
[2. 聚合类型分类详解](#2. 聚合类型分类详解)
[2.1 桶聚合(Bucket Aggregations)](#2.1 桶聚合(Bucket Aggregations))
[2.1.1 基础桶聚合](#2.1.1 基础桶聚合)
[2.1.2 特殊桶聚合](#2.1.2 特殊桶聚合)
[2.1.3 高级桶聚合](#2.1.3 高级桶聚合)
[2.2 指标聚合(Metric Aggregations)](#2.2 指标聚合(Metric Aggregations))
[2.2.1 单值指标聚合(Single-value Metrics)](#2.2.1 单值指标聚合(Single-value Metrics))
[2.2.2 多值指标聚合(Multi-value Metrics)](#2.2.2 多值指标聚合(Multi-value Metrics))
[2.3 管道聚合(Pipeline Aggregation)](#2.3 管道聚合(Pipeline Aggregation))
[2.3.1 数学计算类](#2.3.1 数学计算类)
[2.3.2 桶操作类](#2.3.2 桶操作类)
[2.3.3 流程控制类](#2.3.3 流程控制类)
[3. 聚合的作用范围](#3. 聚合的作用范围)
[4. 聚合排序](#4. 聚合排序)
[5. ES聚合分析不精准原因分析](#5. ES聚合分析不精准原因分析)
[6. Elasticsearch 聚合性能优化](#6. Elasticsearch 聚合性能优化)
1. 概述
Elasticsearch 的聚合(Aggregations)功能用于对数据进行统计、分析和分组,类似 SQL 中的 GROUP BY
与聚合函数(如 SUM
、AVG
)。其核心分为桶聚合 、指标聚合 和管道聚合三类
基本语法
聚合查询的语法结构与其他查询相似,通常包含以下部分:
-
查询条件:指定需要聚合的文档,可以使用标准的 Elasticsearch 查询语法,如 term、match、range 等等。
-
聚合函数:指定要执行的聚合操作,如 sum、avg、min、max、terms、date_histogram 等等。每个聚合命令都会生成一个聚合结果。
-
聚合嵌套:聚合命令可以嵌套,以便更细粒度地分析数据。
GET <index_name>/_search
{
"aggs": {
"<aggs_name>": { // 聚合名称需要自己定义
"<agg_type>": {
"field": "<field_name>"
}
}
}
} -
aggs_name:聚合函数的名称
-
agg_type:聚合种类,比如是桶聚合(terms)或者是指标聚合(avg、sum、min、max等)
-
field_name:字段名称或者叫域名。
聚合类型分类
Elasticsearch 聚合操作主要分为三大类:
聚合类型 | 核心功能 | 典型应用场景 |
---|---|---|
桶聚合 | 将文档分组到"桶"中 | 分组统计、分类分析 |
指标聚合 | 对桶内文档进行数值计算 | 平均值、求和等数值计算 |
管道聚合 | 对其他聚合结果进行二次处理 | 复杂统计分析 |
示例数据准备
java
DELETE /employees
#创建索引库
PUT /employees
{
"mappings": {
"properties": {
"age":{
"type": "integer"
},
"gender":{
"type": "keyword"
},
"job":{
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 50
}
}
},
"name":{
"type": "keyword"
},
"salary":{
"type": "integer"
}
}
}
}
PUT /employees/_bulk
{ "index" : { "_id" : "1" } }
{ "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 }
{ "index" : { "_id" : "2" } }
{ "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000}
{ "index" : { "_id" : "3" } }
{ "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 }
{ "index" : { "_id" : "4" } }
{ "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000}
{ "index" : { "_id" : "5" } }
{ "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 }
{ "index" : { "_id" : "6" } }
{ "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000}
{ "index" : { "_id" : "7" } }
{ "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 }
{ "index" : { "_id" : "8" } }
{ "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000}
{ "index" : { "_id" : "9" } }
{ "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 }
{ "index" : { "_id" : "10" } }
{ "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000}
{ "index" : { "_id" : "11" } }
{ "name" : "Jenny","age":36,"job":"Java Programmer","gender":"female","salary":38000 }
{ "index" : { "_id" : "12" } }
{ "name" : "Mcdonald","age":31,"job":"Java Programmer","gender":"male","salary": 32000}
{ "index" : { "_id" : "13" } }
{ "name" : "Jonthna","age":30,"job":"Java Programmer","gender":"female","salary":30000 }
{ "index" : { "_id" : "14" } }
{ "name" : "Marshall","age":32,"job":"Javascript Programmer","gender":"male","salary": 25000}
{ "index" : { "_id" : "15" } }
{ "name" : "King","age":33,"job":"Java Programmer","gender":"male","salary":28000 }
{ "index" : { "_id" : "16" } }
{ "name" : "Mccarthy","age":21,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : { "_id" : "17" } }
{ "name" : "Goodwin","age":25,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : { "_id" : "18" } }
{ "name" : "Catherine","age":29,"job":"Javascript Programmer","gender":"female","salary": 20000}
{ "index" : { "_id" : "19" } }
{ "name" : "Boone","age":30,"job":"DBA","gender":"male","salary": 30000}
{ "index" : { "_id" : "20" } }
{ "name" : "Kathy","age":29,"job":"DBA","gender":"female","salary": 20000}
2. 聚合类型分类详解
2.1桶聚合(Bucket Aggregations)
按照一定的规则,将文档分配到不同的桶中,从而达到分类的目的。将文档按规则分组到"桶"中,类似 SQL 的 GROUP BY。
桶聚合类型分类
2.1.1 基础桶聚合
聚合类型 | 功能描述 | 示例场景 |
---|---|---|
terms | 按字段值分组 | 按品牌统计商品数量 |
range | 按数值范围分组 | 按价格区间统计商品 |
date_range | 按日期范围分组 | 按时间段统计销售额 |
histogram | 按固定间隔分组 | 每100元价格段统计商品数 |
date_histogram | 按时间间隔分组 | 每月/每周统计销售额 |
geo_distance | 按地理位置距离分组 | 按距离商圈范围统计门店 |
注意:
- Terms,需要字段支持filedata
- keyword 默认支持fielddata
- text需要在Mapping 中开启fielddata,会按照分词后的结果进行分桶
2.1.2 特殊桶聚合
聚合类型 | 功能描述 | 示例场景 |
---|---|---|
filters | 使用自定义过滤器分组 | 同时统计多个过滤条件结果 |
nested | 处理嵌套对象分组 | 统计订单中的商品分类 |
reverse_nested | 从嵌套对象返回父文档分组 | 统计含特定商品的订单 |
children | 在父子文档关系中进行分组 | 统计作者及其书籍 |
composite | 多维度组合分组(替代多级terms) | 品牌+颜色组合统计 |
sampler | 获取代表性样本分组 | 大数据集抽样分析 |
2.1.3 高级桶聚合
聚合类型 | 功能描述 | 示例场景 |
---|---|---|
significant_terms | 识别统计显著性高的词项 | 发现异常关键词 |
geohash_grid | 按地理网格分组 | 区域热力图分析 |
variable_width | 自动调整数值区间宽度分组 | 智能价格分段 |
adjacency_matrix | 关系矩阵分组 | 用户行为关联分析 |
示例
获取job的分类信息
java
# 对keword 进行聚合
GET /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
}
}
}
}
聚合可配置属性有:
- field:指定聚合字段
- size:指定聚合结果数量
- order:指定聚合结果排序方式
默认情况下,Bucket聚合会统计Bucket内的文档数量,记为_count,并且按照_count降序排序。我们可以指定order属性,自定义聚合的排序方式:
java
GET /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword",
"size": 10,
"order": {
"_count": "desc"
}
}
}
}
}
限定聚合范围
java
#只对salary在10000元以上的文档聚合
GET /employees/_search
{
"query": {
"range": {
"salary": {
"gte": 10000
}
}
},
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword",
"size": 10,
"order": {
"_count": "desc"
}
}
}
}
}
注意:对 Text 字段进行 terms 聚合查询,会失败抛出异常
解决办法:对 Text 字段打开 fielddata,支持terms aggregation
java
# 对 Text 字段进行 terms 聚合查询,会失败抛出异常
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job"
}
}
}
}
#对 Text 字段打开 fielddata,支持terms aggregation
PUT /employees/_mapping
{
"properties" : {
"job":{
"type": "text",
"fielddata": true
}
}
}
# 对 Text 字段进行分词,分词后的terms
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job"
}
}
}
}
Range & Histogram聚合
- 按照数字的范围,进行分桶
- 在Range Aggregation中,可以自定义Key
Range 示例:按照工资的 Range 分桶
java
Salary Range分桶,可以自己定义 key
POST employees/_search
{
"size": 0,
"aggs": {
"salary_range": {
"range": {
"field":"salary",
"ranges":[
{
"to":10000
},
{
"from":10000,
"to":20000
},
{
"key":">20000",
"from":20000
}
]
}
}
}
}
Histogram示例:按照工资的间隔分桶
java
#工资0到10万,以 5000一个区间进行分桶
POST employees/_search
{
"size": 0,
"aggs": {
"salary_histrogram": {
"histogram": {
"field":"salary",
"interval":5000,
"extended_bounds":{
"min":0,
"max":100000
}
}
}
}
}
top_hits应用场景: 当获取分桶后,桶内最匹配的顶部文档列表
java
# 指定size,不同工种中,年纪最大的3个员工的具体信息
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
},
"aggs":{
"old_employee":{
"top_hits":{
"size":3,
"sort":[
{
"age":{
"order":"desc"
}
}
]
}
}
}
}
}
}
嵌套聚合示例
java
# 嵌套聚合1,按照工作类型分桶,并统计工资信息
POST employees/_search
{
"size": 0,
"aggs": {
"Job_salary_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"salary": {
"stats": {
"field": "salary"
}
}
}
}
}
}
# 多次嵌套。根据工作类型分桶,然后按照性别分桶,计算工资的统计信息
POST employees/_search
{
"size": 0,
"aggs": {
"Job_gender_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"gender_stats": {
"terms": {
"field": "gender"
},
"aggs": {
"salary_stats": {
"stats": {
"field": "salary"
}
}
}
}
}
}
}
}
2.2 指标聚合(Metric Aggregations)
指标聚合主要用于对文档进行数值计算,分为两大类:
2.2.1 单值指标聚合(Single-value Metrics)
聚合类型 | 功能描述 | 公式/说明 |
---|---|---|
avg | 计算平均值 | sum(field)/doc_count |
sum | 计算总和 | Σ(field_values) |
min | 获取最小值 | min(field_values) |
max | 获取最大值 | max(field_values) |
value_count | 统计非空值数量 | count(field_values) |
cardinality | 计算唯一值数量(去重计数) | distinct_count(field) |
2.2.2 多值指标聚合(Multi-value Metrics)
聚合类型 | 功能描述 | 包含指标 |
---|---|---|
stats | 基础统计汇总 | count, min, max, avg, sum |
extended_stats | 扩展统计(含标准差/方差) | stats + std_deviation, variance |
percentiles | 计算百分位值(如P95/P99) | [P50, P75, P90, P95] |
percentile_ranks | 计算值的百分位排名 | 值X的百分位位置 |
top_hits | 返回每个分组的最相关文档 | 子聚合中的特殊类型 |
geo_centroid | 计算地理坐标中心点 | lat/lon平均值 |
median_absolute_deviation | 计算中位数绝对偏差 | 稳健统计离散度 |
示例
查询员工的最低最高和平均工资
java
#多个 Metric 聚合,找到最低最高和平均工资
POST /employees/_search
{
"size": 0,
"aggs": {
"max_salary": {
"max": {
"field": "salary"
}
},
"min_salary": {
"min": {
"field": "salary"
}
},
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
}
2.3 管道聚合(Pipeline Aggregation)
对聚合分析的结果,再次进行聚合分析。
2.3.1 数学计算类
聚合类型 | 功能 | 公式示例 |
---|---|---|
cumulative_sum | 累积和 | Sₙ = a₁ + a₂ + ... + aₙ |
derivative | 变化率 | Δy/Δx = (y₂-y₁)/(x₂-x₁) |
moving_function | 移动计算 | SMA = (P₁+P₂+...+Pₙ)/n |
2.3.2 桶操作类
聚合类型 | 功能 | 类似SQL |
---|---|---|
max/min/avg/sum_bucket | 桶值极值/平均 | SELECT MAX(subtotal) |
stats/extended_stats_bucket | 桶统计信息 | SELECT STATS(subtotal) |
percentiles_bucket | 桶百分位值 | SELECT PERCENTILE(subtotal, 95) |
2.3.3 流程控制类
聚合类型 | 功能 | 类似编程概念 |
---|---|---|
bucket_selector | 桶过滤 | HAVING 子句 |
bucket_sort | 桶排序 | ORDER BY |
bucket_script | 自定义脚本 | 函数计算 |
min_bucket示例
在员工数最多的工种里,找出平均工资最低的工种
java
# 平均工资最低的工种
POST employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}
- min_salary_by_job结果和jobs的聚合同级
- min_bucket求之前结果的最小值
- 通过bucket_path关键字指定路径
Stats示例
java
# 平均工资的统计分析
POST employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"stats_salary_by_job":{
"stats_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}
percentiles示例
java
# 平均工资的百分位数
POST employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"percentiles_salary_by_job":{
"percentiles_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}
Cumulative_sum示例
java
#Cumulative_sum 累计求和
POST employees/_search
{
"size": 0,
"aggs": {
"age": {
"histogram": {
"field": "age",
"min_doc_count": 0,
"interval": 1
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
},
"cumulative_salary":{
"cumulative_sum": {
"buckets_path": "avg_salary"
}
}
}
}
}
}
3. 聚合的作用范围
ES聚合分析的默认作用范围是query的查询结果集,同时ES还支持以下方式改变聚合的作用范围:
- Filter
- Post Filter
- Global
示例
java
#Query
POST employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 20
}
}
},
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
}
}
}
}
#Filter
POST employees/_search
{
"size": 0,
"aggs": {
"older_person": {
"filter":{
"range":{
"age":{
"from":35
}
}
},
"aggs":{
"jobs":{
"terms": {
"field":"job.keyword"
}
}
}},
"all_jobs": {
"terms": {
"field":"job.keyword"
}
}
}
}
#Post field. 一条语句,找出所有的job类型。还能找到聚合后符合条件的结果
POST employees/_search
{
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword"
}
}
},
"post_filter": {
"match": {
"job.keyword": "Dev Manager"
}
}
}
#global
# 使用global聚合来计算所有匹配查询的文档(即所有年龄大于或等于40岁的员工)的平均薪资。
#global聚合的特点是它会考虑查询范围内的所有文档,而不仅仅是某个特定分组或桶中的文档。
POST employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 40
}
}
},
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
}
},
"all":{
"global":{},
"aggs":{
"salary_avg":{
"avg":{
"field":"salary"
}
}
}
}
}
}
4. 聚合排序
指定order,按照count和key进行排序:
- 默认情况,按照count降序排序
- 指定size,就能返回相应的桶
java
#排序 order
#count and key
POST employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 20
}
}
},
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword",
"order":[
{"_count":"asc"},
{"_key":"desc"}
]
}
}
}
}
#排序 order
#count and key
POST employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword",
"order":[ {
"avg_salary":"desc"
}]
},
"aggs": {
"avg_salary": {
"avg": {
"field":"salary"
}
}
}
}
}
}
#排序 order
#count and key
POST employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword",
"order":[ {
"stats_salary.min":"desc"
}]
},
"aggs": {
"stats_salary": {
"stats": {
"field":"salary"
}
}
}
}
}
}
5. ES聚合分析不精准原因分析
ElasticSearch在对海量数据进行聚合分析的时候会损失搜索的精准度来满足实时性的需求。

Terms聚合分析的执行流程:

不精准的原因: 数据分散到多个分片,聚合是每个分片的取 Top X,导致结果不精准。ES 可以不每个分片Top X,而是全量聚合,但势必这会有很大的性能问题。
思考:如何提高聚合精确度?
方案1:设置主分片为1
注意7.x版本已经默认为1。
适用场景:数据量小 的小集群规 模业务场景。
方案2:调大 shard_size 值
设置 shard_size 为比较大的值,官方推荐:size*1.5+10。shard_size 值越大,结果越趋近于精准聚合结果值。此外,还可以通过show_term_doc_count_error参数显示最差情况下的错误值,用于辅助确定 shard_size 大小。
- size:是聚合结果的返回值,客户期望返回聚合排名前三,size值就是 3。
- shard_size: 每个分片上聚合的数据条数。shard_size 原则上要大于等于 size
适用场景:数据量大、分片数多的集群业务场景。
测试: 使用kibana的测试数据
java
DELETE my_flights
PUT my_flights
{
"settings": {
"number_of_shards": 20
},
"mappings" : {
"properties" : {
"AvgTicketPrice" : {
"type" : "float"
},
"Cancelled" : {
"type" : "boolean"
},
"Carrier" : {
"type" : "keyword"
},
"Dest" : {
"type" : "keyword"
},
"DestAirportID" : {
"type" : "keyword"
},
"DestCityName" : {
"type" : "keyword"
},
"DestCountry" : {
"type" : "keyword"
},
"DestLocation" : {
"type" : "geo_point"
},
"DestRegion" : {
"type" : "keyword"
},
"DestWeather" : {
"type" : "keyword"
},
"DistanceKilometers" : {
"type" : "float"
},
"DistanceMiles" : {
"type" : "float"
},
"FlightDelay" : {
"type" : "boolean"
},
"FlightDelayMin" : {
"type" : "integer"
},
"FlightDelayType" : {
"type" : "keyword"
},
"FlightNum" : {
"type" : "keyword"
},
"FlightTimeHour" : {
"type" : "keyword"
},
"FlightTimeMin" : {
"type" : "float"
},
"Origin" : {
"type" : "keyword"
},
"OriginAirportID" : {
"type" : "keyword"
},
"OriginCityName" : {
"type" : "keyword"
},
"OriginCountry" : {
"type" : "keyword"
},
"OriginLocation" : {
"type" : "geo_point"
},
"OriginRegion" : {
"type" : "keyword"
},
"OriginWeather" : {
"type" : "keyword"
},
"dayOfWeek" : {
"type" : "integer"
},
"timestamp" : {
"type" : "date"
}
}
}
}
POST _reindex
{
"source": {
"index": "kibana_sample_data_flights"
},
"dest": {
"index": "my_flights"
}
}
GET my_flights/_count
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"weather": {
"terms": {
"field":"OriginWeather",
"size":5,
"show_term_doc_count_error":true
}
}
}
}
GET my_flights/_search
{
"size": 0,
"aggs": {
"weather": {
"terms": {
"field":"OriginWeather",
"size":5,
"shard_size":10,
"show_term_doc_count_error":true
}
}
}
}
在Terms Aggregation的返回中有两个特殊的数值:
- doc_count_error_upper_bound : 被遗漏的term 分桶,包含的文档,有可能的最大值
- sum_other_doc_count: 除了返回结果 bucket的terms以外,其他 terms 的文档总数(总数-返回的总数
方案3:将size设置为全量值,来解决精度问题
将size设置为2的32次方减去1也就是分片支持的最大值,来解决精度问题。
原因:1.x版本,size等于 0 代表全部,高版本取消 0 值,所以设置了最大值(大于业务的全量值)。
全量带来的弊端就是:如果分片数据量极大,这样做会耗费巨大的CPU 资源来排序,而且可能会阻塞网络。
适用场景:对聚合精准度要求极高的业务场景,由于性能问题,不推荐使用。
方案4:使用Clickhouse/ Spark 进行精准聚合
适用场景:数据量非常大、聚合精度要求高、响应速度快的业务场景。
6. Elasticsearch 聚合性能优化
插入数据时对索引进行预排序
- Index sorting (索引排序)可用于在插入时对索引进行预排序,而不是在查询时再对索引进行排序,这将提高范围查询(range query)和排序操作的性能。
- 在 Elasticsearch 中创建新索引时,可以配置如何对每个分片内的段进行排序。
- 这是 Elasticsearch 6.X 之后版本才有的特性。
java
PUT /my_index
{
"settings": {
"index":{
"sort.field": "create_time",
"sort.order": "desc"
}
},
"mappings": {
"properties": {
"create_time":{
"type": "date"
}
}
}
}
注意:预排序将增加 Elasticsearch 写入的成本。在某些用户特定场景下,开启索引预排序会导致大约 40%-50% 的写性能下降。也就是说,如果用户场景更关注写性能的业务,开启索引预排序不是一个很好的选择。
使用节点查询缓存
节点查询缓存(Node query cache)可用于有效缓存过滤器(filter)操作的结果。如果多次执行同一 filter 操作,这将很有效,但是即便更改过滤器中的某一个值,也将意味着需要计算新的过滤器结果。
你可以执行一个带有过滤查询的搜索请求,Elasticsearch将自动尝试使用节点查询缓存来优化性能。例如,如果你想缓存一个基于特定字段值的过滤查询,你可以发送如下的HTTP请求:
java
GET /your_index/_search
{
"query": {
"bool": {
"filter": {
"term": {
"your_field": "your_value"
}
}
}
}
}
使用分片请求缓存
聚合语句中,设置:size:0,就会使用分片请求缓存缓存结果。size = 0 的含义是:只返回聚合结果,不返回查询结果。
java
GET /es_db/_search
{
"size": 0,
"aggs": {
"remark_agg": {
"terms": {
"field": "remark.keyword"
}
}
}
}
拆分聚合,使聚合并行化
Elasticsearch 查询条件中同时有多个条件聚合,默认情况下聚合不是并行运行的。当为每个聚合提供自己的查询并执行 msearch 时,性能会有显著提升。因此,在 CPU 资源不是瓶颈的前提下,如果想缩短响应时间,可以将多个聚合拆分为多个查询,借助:msearch 实现并行聚合。
java
#常规的多条件聚合实现
GET /employees/_search
{
"size": 0,
"aggs": {
"job_agg": {
"terms": {
"field": "job.keyword"
}
},
"max_salary":{
"max": {
"field": "salary"
}
}
}
}
# msearch 拆分多个语句的聚合实现
GET _msearch
{"index":"employees"}
{"size":0,"aggs":{"job_agg":{"terms":{"field": "job.keyword"}}}}
{"index":"employees"}
{"size":0,"aggs":{"max_salary":{"max":{"field": "salary"}}}}