前言
最近在公司做了一个类似大盘、看板的功能,所有的数据全部存储在ES里面,完全需要自己去ES里面捞数据,并没有一个结构化、过滤好的数据源,完全需要自己对ES的数据进行过滤、筛选、统计,所以就需要用到聚合了,下面是功能图,在图里面其实展示了很多重要信息
- 根据时间范围、其他条件进行筛选数据
- 指标数据以每天为单位进行展示
- 统计所有数据,变成总量数据
这里就需要考验到对ES聚合的使用了,因为不可能查询每个指标,都访问一遍ES,不仅指标多,指标也要根据日期进行分桶,不可能每次都发一次请求,即便是多线程,那也对ES压力太大了,使用了过多的网络资源来满足需求,况且时间范围越多,请求次数也越多,所以很不合理
这个时候就体现出来聚合的重要性了,ES底层自己支持大量的聚合方式,可以满足按天为单位聚合数据,展示每天数据量有多少,也可以在其上层进行过滤,展示某个数据每天有多少 (不得不说,这简直太棒了)
底层的指标都是我来计算提供数据的,所以对这块很熟,也使用了策略设计模式去做这个内容,满足其拓展性
聚合是什么
首先要明白,聚合是什么
聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段(或计算表达式的结果)的最大值、最小值,计算和、平均值等。ES作为搜索引擎兼数据库,同样提供了强大的聚合分析能力。
对一个数据集求最大、最小、和、平均值等指标的聚合,在ES中称为指标聚合(metric)
而关系型数据库中除了有聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行指标聚合。在ES中group by称为分桶,桶聚合bucketing
通过聚合,我们可以统计出绝大部分的数据,也可以查出来自己想要的指标,满足其使用性
用法
按天聚合数据
{
"aggs": {
"daily_hits_true": {
"filter": {
"bool": {
"must": [
{
"range": {
"statDate": {
"gte": 1700668800000,
"lt": 1701359999000
}
}
},
{
"nested": {
"path": "searchFields",
"query": {
"bool": {
"must": [
{
"term": {
"searchFields.name": "C_S_TDRESULT"
}
},
{
"term": {
"searchFields.value.raw": "true"
}
}
]
}
}
}
}
]
}
},
"aggs": {
"daily_hits": {
"date_histogram": {
"field": "statDate",
"interval": "1d",
"format": "yyyy-MM-dd"
}
}
}
}
}
}
上面的聚合就是一个非常简单的例子,我根据时间范围(statDate)进行筛选数据,判断C_S_TDRESULT是否为true进行过滤
最后添加一个 daily_hits
聚合条件统计出每天的数据
这里的聚合使用的是ES的 date_histogram
,他可以按照指定时间范围的内容进行统计,例如几小时,也是完全可以的
field: 根据哪个字段进行统计
interval: 时间周期是多少,1d就是按照一天为单位统计数据
format: 展示出来的时间格式是什么,如果按照1小时统计,需要填写小时的单位,需求是按照天统计,所以不关注时分秒的内容
去重聚合
去重聚合顾名思义,假如你数据存在一个 PHONE_NUMBER
字段
- 第1条数据: PHONE_NUMBER = qwe
- 第2条数据: PHONE_NUMBER = abc
- 第3条数据: PHONE_NUMBER = qwe
最终去重聚合统计出来的结果是2,这个2的数据就是聚合后的结果
{
"aggs": {
"daily_hits": {
"date_histogram": {
"field": "statDate",
"interval": "1d",
"format": "yyyy-MM-dd"
},
"aggregations": {
"searchFields": {
"nested": {
"path": "searchFields"
},
"aggregations": {
"relativeInAcNumFilter": {
"filter": {
"bool": {
"must": [
{
"term": {
"searchFields.name": "S_S_MOBINO"
}
}
]
}
},
"aggregations": {
"relativeInAcNum": {
"cardinality": {
"field": "searchFields.value.raw"
}
}
}
}
}
}
}
}
}
}
顶部的 daily_hits
就是按天聚合,主要是看最里面的 relativeInAcNum
聚合
其中的 cardinality
才是去重的关键,他根据 searchFields.name = S_S_MOBINO
内容进行去重
后话
虽然这样聚合可以满足我们的指标结果,当指标类型过多时,还是会产生一定的ES压力,并且也要根据时间范围而定,当数据量特别大的情况下,ES查询效率也会变慢,有时候倒不如直接多线程执行多条ES操作了
主要选择还是在于自身的体量上,一般来说都放在ES上直接进行大聚合,一次操作,查询多个结果,这样也要求ES需要极高的效率,虽然ES是一个搜索效率极高的组件,有时候也需要考虑他的性能
这里其实还有很多可以优化的点,因为按天搜索出来的数据,只有当天的数据需要保证实时性,也就是聚合的数据只需要查找当天的即可,所以可以在每天的半夜,统计昨天一整天的数据,落库存储至数据库,以此来减轻ES的压力。当存储在ES的数据量越多,聚合的效率也会降低,压力也会飙升,如若时间范围更长,筛选的数据也会更多,所以将死数据存入数据库类似的介质或许是一种选择
2 条评论
这是一篇佳作,无论是从内容、语言还是结构上,都堪称完美。
多语种文献的引用彰显学术包容性。