高基数 GroupBy 在 SLS SQL 中的查询加速
作者:顾汉杰(执少)
什么是高基数 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 中,但因分片数以及数据分布特征不尽相同,因此不同用户的数据实测结果可能略有差异,但相同量级应该大同小异。
🚗 走,上车!
案例 1:
高基单列聚合,对 28 亿条请求日志,按 RequestId 字段进行 GroupBy 统计计数(实际基数为 28 亿)
第一步,进行基准测试,使用普通 SQL 模式,查询大约需要 17s。
第二步,切换到增强 SQL 模式,并设置 session 参数 set session hash_partition_count=40(此值默认最大为 20),查询降到 10s。
这是什么魔法操作?
上文提到底层并行度已经足够分散,但我们面对的是一个 28 亿条绝对高基的测试数据集(每一个 RequestId 都不一样),即使底层并行度足够,FinalAgg 阶段仍将面临极大的聚合计算压力,而我们默认 FinalAgg 阶段的并行度与 shard 分片数相关但最大为 20,这里我们将这个能力开放给用户结合自身情况可以进行动态调整。
第三步,设置 session 参数 set session hash_partition_count=64/128/200,查询进一步降到 7s/4.5s/3.7s。
加速效果还不错。。。
通过增加 FinalAgg 阶段的并行度,我们看到查询从原来的 17s 降低到了 3.7s!同时,我们也看到并行度的增加对于查询延时的加速效果也逐渐收敛,这是因为在增加并行度的同时,也增加了额外的网络通信和调度开销。所以我们为该 session 参数设置了系统上限为 200,再往上其实可能也不会带来太大的加速收益。
以为这样就收工了,并没有!
第四步,设置 session 参数 set session high_cardinality_agg=true,查询降到 2.1s!
这又是发生了什么?
针对高基数场景的数据特征,我们将数据在底层走了一个不同的流转模式进行数据聚合,大幅提高效率。我们同样开放了相应的 session 参数供用户按需使用。
最终,高基单列聚合,我们将查询从原来的 17s 降至 2s,体验到了 8 倍的加速快感。
案例 2:
高基多列聚合,对 45 亿条请求日志,进行多列 GroupBy 统计计数(实际多列组合维基为 15 亿)
第一步,进行基准测试,使用普通 SQL 模式,查询大约需要 24s。
第二步,切换到增强 SQL 模式,并设置 session 参数 set session hash_partition_count=40(此值默认最大为 20),查询降 到11s。
第三步,提升并行度 set session hash_partition_count=64/128/200,查询进一步降到 7.3s/5.9s/5.8s。
第四步,设置 session 参数 set session high_cardinality_agg=true,查询降到 2.9s!
最终,高基多列聚合,我们将查询从原来的 24s 降至 3s,同样也体验到了 8 倍的加速快感。
案例 3:
低基数值聚合,对 1.5 万亿条请求日志,按 Latency 字段统计 Top100 的频次(实际基数为 735 万)
第一步,进行基准测试,使用普通 SQL 模式,查询虽然只有 4.3s,但是因数据量大小限制被截断而结果不精确。
第二步,切换到增强 SQL 模式,查询只需 23.4s 即可精确查询,此时增强 SQL 体现出在面对超大规模数据量时的威力。
第三步,设置 session 参数 set session hash_partition_count=40/64/128/200(此值默认最大为 20),查询延时维持到 22-23s 之间,可以看到,在低基聚合场景中,通过提升 FinalAgg 并行度来加速查询,效果十分有限。
通过系统的监控分析,可以发现此时计算压力主要在 PartialAgg,FinalAgg 并不存在计算瓶颈,因此对它的提升,并不会对整体查询有明显的加速效果。
第四步,让我们再试试高基优化参数 set session high_cardinality_agg=true,结果是执行超时。
这说明,高基优化参数并不适用于低基聚合场景,在低基维度下,面对超大规模数据(测试数据超过 1.5 万亿)的聚合计算,默认的预聚合技术(PartialAgg+FinalAgg)能够有效发挥计算效能,仍然是不二之选。
结论和建议
本文主要介绍了 SLS 中的高基数 GroupBy 查询加速原理,并设计了 3 种典型场景的测试用例,通过 SLS 线上服务的实际测试表现,带大家体验了高基聚合计算 8 倍查询加速、万亿数据 20s 查询的极致快感。
在测试过程中,也一一向读者解释了其中的具体细节和原理,为什么会这样?为什么能加速?为什么加速不明显等等。以下是给 SLS 用户关于此方面的查询实践和建议:
- 数据规模不大时,使用默认模式即可;
- 数据规模很大而数据分片不多时,建议开启增强 SQL 模式,可以极大提升数据底层并行度;
- 高基聚合时,可以尝试以下两个 session 参数,可能带来数倍查询加速效果:
- set session hash_partition_count= 设置多少合适?建议 20-64 以内,过犹不及
- set session high_cardinality_agg=true/false 是否设置取决于数据离散度
- 低基聚合,以上两个 session 参数并不十分适用,通过默认增强 SQL 模式即可实现查询加速和精确。
相关文章:

高基数 GroupBy 在 SLS SQL 中的查询加速
作者:顾汉杰(执少) 什么是高基数 GroupBy 简单来说,想要分析的数据,拥有超多的“唯一值计数”(Distinct Count),而我们需要对这些数据进行分组分析(如统计次数、排名、…...
TP5队列和TP5 使用redis 等相关
TP5.thinkphp之门面(facade类)面试_thinkphp facade-CSDN博客 TP5中的消息队列_tp 5.0 队列 release 时间单位-CSDN博客 thinkphp-queue自带的队列包使用分析_php think queue:listen-CSDN博客TP5 使用redis_tp5 redis-CSDN博客...

【R语言速通】1.数据类型
文章目录 0. 变量名1.基本数据类型1.1 数值型1.2 整型1.3 复数型1.4 逻辑型1.5 字符型 2.复合数据类型2.1 向量向量操作向量的常用函数 2.2 矩阵矩阵操作矩阵的常用函数 2.3 数组数组的操作数据的运算数组的访问数组的维度操作 数组的常用函数 2.4 数据框数据框操作数据框的常用…...
【C++设计模式】(三)创建型模式:单例模式
文章目录 (三)创建型模式:单例模式饿汉式懒汉式饿汉式 v.s. 懒汉式 (三)创建型模式:单例模式 单例模式在于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在某些情况下࿰…...

基于Android Studio的行程记录APK开发指南(三)---界面设计及两种方法获取用户位置
前言 本系列教程我们来看看如何使用Android Studio去开发一个APK用于用户的实时行程记录 第一期:基于Android Studio的用户行程记录APK开发指南(一):项目基础配置与速通Kotlin-CSDN博客第二期:基于Android Studio的行程记录APK开发指南(二):…...
大厂趋势:低代码不等于低能力,赋能高效开发新纪元
大厂趋势:低代码不等于低能力,赋能高效开发新纪元 在数字化转型的浪潮中,科技巨头(大厂)作为行业的引领者,不断探索和创新,以应对日益复杂多变的市场需求和技术挑战。其中,“低代码…...

CentOS全面停服,国产化提速,央国企信创即时通讯/协同门户如何选型?
01. CentOS停服带来安全新风险, 国产操作系统迎来新的发展机遇 2024年6月30日,CentOS 7版本全面停服,于2014年发布的开源类服务器操作系统——CentOS全系列版本生命周期画上了句号。国内大量基于CentOS开发和适配的服务器及平台,…...

如何确定Kubernetes是在采用哪种方式进行部署的?
这里写目录标题 1. 查看 Kubernetes 安装方式的常见文件和工具2. 检查 Kubernetes 的节点信息3. 检查 Kubernetes API 服务器的版本信息4. 检查系统服务和容器5. 查看安装文档或管理员笔记为什么可以确定是 kubeadm 部署?下一步确认 如果存在多个master节点…...

【PostgreSQL】地理空间数据的数据类型定义、索引优化、查询优化策略
PostgreSQL 是开源关系型数据库,对于地理空间数据的处理提供了很好的支持。在处理地理空间数据时,优化索引和查询的性能至关重要,因为地理空间操作通常涉及大量的数据计算和复杂的几何形状比较。 一、地理空间数据类型 注意geometry和geogra…...
RocketMQ广播消费消息
1、 基础概念 RocketMQ 支持两种消息模式:集群消费( Clustering )和广播消费( Broadcasting )。 集群消费模式(Cluster): 在集群消费模式下,同一个消费者组(…...
C#基础(2)枚举
前言 我们其实在前面已经了解过枚举到底有什么作用,但是那毕竟是概念性的语言,理解起来很抽象,今天我们会具体来讲一讲枚举,并谈一谈它的应用。 希望你能从今天的C#基础中有所收获。 基本概念 1.枚举:是一个比较特…...

Linux之MySQL日志
前言 数据库就像一个庞大的图书馆,而日志则是记录这个图书馆内每一本书的目录。正如在图书馆中找到特定书籍一样,数据库日志帮助我们追溯数据的变更、定位问题和还原状态。 在MySQL中,日志是非常重要的一个组成部分,它记录了数据…...

Redis集群模式—主从集群、哨兵集群、分片集群
主从集群 主从模式中,包括一个主节点(Master)和一个或多个从节点(Slave)。主节点负责处理所有写操作和读操作,而从节点则复制主节点的数据,并且只能处理读操作。当主节点发生故障时,…...

并发工具类(二):CyclicBarrier
1、CyclicBarrier 介绍 从字面上看 CyclicBarrier 就是 一个循环屏障,它也是一个同步助手工具,它允许多个线程 在执行完相应的操作后彼此等待共同到达一个屏障点。 CyclicBarrier可以被循环使用,当屏障点值变为0之后,可以在接下来…...
Spring Cloud全解析:负载均衡之Ribbon简介
Ribbon简介 Ribbon是一种客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起,提供了一系列完善的配置如连接超时、重试等,Ribbon会自动的帮助基于某种规则(如简单轮询、随机连接等)去连接那些机器,也可以自定义的负载均衡…...
Kettle安装与使用指南
1. 介绍 什么是Kettle? Kettle,全称Pentaho Data Integration (PDI),是Pentaho BI套件的一部分。它提供了一个可视化的ETL工具,允许用户通过图形界面设计复杂的数据集成流程。Kettle支持多种数据源,包括关系型数据库…...

教育行业解决方案:智能PPT在教育行业的创新应用
在信息化时代,教育行业面临着巨大的变革。随着人工智能技术的不断发展,传统教学方式正在被重新定义。彩漩科技作为 AI 技术的先行者,推出了歌者 PPT &彩漩 PPT,为教师、学生和家长提供了一种全新的教育体验,实现了…...

Matlab程序练习
Part1 1.求 [100,999] 之间能被 21整除的数的个数。 程序: 主文件:main.m clear; start_num 100; end_num 999; div_num 21; res div(start_num,end_num,div_num); fprintf("[%d,%d]之间能被%d整除的数的个数为%d个\n",start_num,end_…...

cesium可不可以改变影像底图颜色,如何给地球底图影像添加一层滤镜蒙版?
废话:你的球是不是很丑?是不是没有科技感?是不是没有好看的影像? 因果: 因:客户问,底图可不可以改变颜色,想让球更漂亮一些。 答:可以改变影像饱和度,透明度…...

MyBatis-MappedStatement什么时候生成?QueryWrapper如何做到动态生成了SQL?
通过XML配置的MappedStatement 这部分MappedStatement主要是由MybatisXMLMapperBuilder进行解析,核心逻辑如下: 通过注解配置的MappedStatement 核心逻辑就在这个里面了: 继承BaseMapper的MappedStatement 我们看看这个类,里…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
算法250609 高精度
加法 #include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<algorithm> using namespace std; char input1[205]; char input2[205]; int main(){while(scanf("%s%s",input1,input2)!EOF){int a[205]…...
SQL进阶之旅 Day 22:批处理与游标优化
【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...

Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...