作者:顾汉杰(执少)
什么是高基数 GroupBy
简单来说,想要分析的数据,拥有超多的“唯一值计数”(Distinct Count),而我们需要对这些数据进行分组分析(如统计次数、排名、计算均值、分位值等)。
高基数聚合计算在很多运营分析场景中都是刚需,它涉及对值不一样的海量数据进行分组聚合计算,以洞察用户行为、游戏玩家路径、市场趋势或产品表现等运营分析的关键指标。例如,在电商平台上,分析一段时间内不同商品类别在各个地区的销量分布,或者在游戏运营分析场景中,追踪玩家在游戏中的独特操作行为和路径,这些都需要处理基数极高的数据(如 ItemId、RequestId、TraceId 等,动辄上千万甚至亿级别的基数)。
现在的问题是,用户在进行此类分析时,由于数据量和复杂度的不同,SQL 执行耗时往往可能从数秒到数分钟甚至数小时不等,“高基数 GroupBy 执行太慢”,几乎成为用户的普遍认知,也是众多数据库和 OLAP 引擎重点关注的对象。SLS SQL 也持续关注这一点,并对此进行了相应的性能优化,本文即旨在向用户介绍 SLS 中的实现原理、查询加速手段以及适用场景。
GroupBy 的实现原理
GroupBy 是几乎所有 OLAP 引擎必备的基础聚合能力,分布式计算引擎一般将海量数据以 Hash 散列的方式分布到不同节点进行分组(分桶)计算,每个分组内对数据进行聚合,然后再基于堆(往往使用 PriorityQueue)进行排序或 Limit,最终输出给用户需要的数据,比如 TopN 排行结果等。
这个过程中,我们可能还会用到预聚合技术:利用数据的局部性原理,对原始数据进行预聚合(PartialAgg),然后再发往最终聚合节点(FinalAgg),以减少网络间数据传输开销。
所以,总体来说,GroupBy 聚合计算大致会经历以下四个过程:DataSource -> PartialAgg -> FinalAgg -> Output。
其中,DataSource 和 PartialAgg 一般是绑定在一起执行,而 FinalAgg 以及 Output 则由分布式网络中的另外一些节点执行。
体验 SLS 高基数 GroupBy 查询加速
有了上面的基础知识和了解后,我们开门见山,直接带大家来感受一下 SLS 中的上亿级别高基数 GroupBy 的查询加速体验。
为了更客观地评估和分析下面的性能变化,我们必须先讲清楚我们的测试数据和测试用例情况。
测试数据
我们采用了模拟的类似 Nginx 服务访问日志,保存在一个 Project/Logstore 中,SQL独享版 CU 数设置为 5000。测试数据 Schema 如下:
{ RequestId: varchar, /*测试数据会确保每个请求ID确保全局唯一*/ ClientIP: varchar, Method: varchar, Latency: int, Status: int, ...}
测试用例
我们准备了 3 种测试用例,分别对应 3 种不同的业务分析场景:
- 高基单列聚合:对 28 亿条请求日志,按 RequestId 字段进行 GroupBy 统计计数(实际基数为 28 亿)
- 高基多列聚合:对 45 亿条请求日志,按 ClientIp、Status、Latency 字段进行 3 列 GroupBy 统计计数(实际基数为 15 亿)
- 低基数值聚合:对 1.5 万亿条请求日志,按 Latency 字段统计 Top100 的频次(实际基数为 735 万)
测试说明
- 由于我们系统中设计有多级缓存,为了避免缓存对于测试的影响干扰,我们会在每次查询时通过添加 not <不存在的keyword> 过滤条件来避开缓存,以确保每次查询都进行完整的物理执行,公平地对比整体执行性能。
- 测试过程使用的是真实线上服务(地域为上海),测试数据真实存储在 SLS Logstore 中,但因分片数以及数据分布特征不尽相同,因此不同用户的数据实测结果可能略有差异,但相同量级应该大同小异。