一、背景:
产品上想要对资产进行聚合,资产的定义是ip+port,在es里是两个字段,目前对着两个字段进行cardinality聚合在客户那直接报(57亿)超时
二、解决方案
将ip+port单独写为一个字段,但是如果对这个字段进行聚合,发现时间还是比较长,因为基数比较大,在这种高基数聚合下,需要参与 hash 运算的数据集合过于庞大。
解决思路:
1、对于字段值,存储Hash值(写入时处理)
2、基于Hash 做聚合和统计分析操作
刚好es7.x之后可以借助mapper-murmur3 插件实现Hash 值类型
PS:
高基数:意味着一个字段包含很大比例的唯一值。
低基数:意味着一个字段包含很多重复的值。
三、测试:
测试1
配置:64g内存 数据磁盘716g Intel(R) Xeon(R) CPU E5-2609 v4 @ 1.70GHz 逻辑cpu数量-8
es:版本-7.16.2 堆内存大小-16g
数据量: 5521711 条数据
将ip port存成一个字段,进行聚合,需要近17s:
由于这个字段是高基数聚合,所以采用提前hash的方式优化,直接聚合hash后的结果只需要近600ms
测试2
配置:64g内存 数据磁盘1.6t Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz 逻辑cpu数量-48
es:版本-7.17.3 堆内存大小-16g
数据量: 14531707 条数据
将ip port存成一个字段,进行聚合,需要3.67s:
对hash值进行聚合结果:
测试3
配置:同测试2
索引: 同测试2
数据量: 39568036 条数据
对ip_port进行聚合,需要12.86s:
对ip_port.hash进行聚合,需要583ms:
四、结论:
在pre hash之后,可以看到聚合速度有了很大提升,并且hash后的聚合时间并不是线性增长的,可以预估57亿的数据聚合耗时应该小于1.4分钟,客户的es配置为内存128G,硬盘24T,32核,理论上性能会更好
五、思考:
虽说pre hash大大提高了聚合的性能,但还是有量变而导致质变的问题,这种方法也只能作为阶段性方案。所以感觉限制数据的统计量,从产品层面去做出某些“妥协”,可能才是最终的解决之道。