SQL调优指南—SQL调优进阶—聚合优化和执行

简介: 本文介绍如何优化器和执行器如何处理聚合(Group-by),以达到减少数据传输量和提高执行效率的效果。

基本概念

聚合操作(Aggregate,简称Agg)语义为按照GROUP BY指定列对输入数据进行聚合的计算,或者不分组、对所有数据进行聚合的计算。PolarDB-X支持如下聚合函数:

  • COUNT
  • SUM
  • AVG
  • MAX
  • MIN
  • BIT_OR
  • BIT_XOR
  • GROUP_CONCAT

聚合(Agg)

本文介绍均为不下推的Agg的实现。如果已被下推到LogicalView中,则由存储层MySQL来选择执行方式,聚合(Agg)由两种主要的算子HashAgg和SortAgg实现。

HashAgg

HashAgg利用哈希表实现聚合:

  1. 根据输入行的分组列的值,通过Hash找到对应的分组。
  2. 按照指定的聚合函数,对该行进行聚合计算。
  3. 重复以上步骤直到处理完所有的输入行,最后输出聚合结果。


> explain select count(*) from t1 join t2 on t1.id = t2.id group by t1.name,t2.name;
Project(count(*)="count(*)")
  HashAgg(group="name,name0", count(*)="COUNT()")
    BKAJoin(condition="id = id", type="inner")
      Gather(concurrent=true)
        LogicalView(tables="t1", shardCount=2, sql="SELECT `id`, `name` FROM `t1` AS `t1`")
      Gather(concurrent=true)
        LogicalView(tables="t2_[0-3]", shardCount=4, sql="SELECT `id`, `name` FROM `t2` AS `t2` WHERE (`id` IN ('?'))")

Explain结果中,HashAgg算子还包含以下关键信息:

  • group:表示GROUP BY字段,示例中为name,name0分别引用t1,t2表的name列,当存在相同别名会通过后缀数字区分 。
  • 聚合函数:等号(=) 前为聚合函数对应的输出列名,其后为对应的计算方法。示例中 count(*)="COUNT()" ,第一个 count(*) 对应输出的列名,随后的COUNT()表示对其输入数据进行计数。

HashAgg对应可以通过Hint来关闭:/*+TDDL:cmd_extra(ENABLE_HASH_AGG=false)*/

SortAgg

SortAgg在输入数据已按分组列排序的情况,对各个分组依次完成聚合。

  • 保证输入按指定的分组列排序(例如,可能会看到 MergeSort 或 MemSort)。
  • 逐行读入输入数据,如果分组与当前分组相同,则对其进行聚合计算。
  • 如果分组与当前分组不同,则输出当前分组上的聚合结果。

相比 HashAgg,SortAgg 每次只要处理一个分组,内存消耗很小;相对的,HashAgg 需要把所有分组存储在内存中,需要消耗较多的内存。


> explain select count(*) from t1 join t2 on t1.id = t2.id group by t1.name,t2.name order by t1.name, t2.name;

Project(count()="count()")
MemSort(sort="name ASC,name0 ASC")
HashAgg(group="name,name0", count(*)="COUNT()")
BKAJoin(condition="id = id", type="inner")
Gather(concurrent=true)
LogicalView(tables="t1", shardCount=2, sql="SELECT `id`, `name` FROM `t1` AS `t1`")
Gather(concurrent=true)
LogicalView(tables="t2_[0-3]", shardCount=4, sql="SELECT `id`, `name` FROM `t2` AS `t2` WHERE (`id` IN ('?'))")

SortAgg对应可以通过Hint来关闭:/+TDDL:cmd_extra(ENABLE_SORT_AGG=false)/

两阶段聚合优化

两阶段聚合,即通过将Agg拆分为部分聚合(Partial Agg)和最终聚合(Final Agg)的两个阶段,先对部分结果集做聚合,然后将这些部分聚合结果汇总,得到整体聚合的结果。

如下示例的SQL中,HashAgg 中拆分出的部分聚合(PartialAgg)会被下推至MySQL上的各个分表,而其中的AVG函数也被拆分成 SUM和 COUNT 以实现两阶段的计算:


> explain select avg(age) from t2 group by name
Project(avg(age)="sum_pushed_sum / sum_pushed_count")
HashAgg(group="name", sum_pushed_sum="SUM(pushed_sum)", sum_pushed_count="SUM(pushed_count)")
Gather(concurrent=true)
LogicalView(tables="t2_[0-3]", shardCount=4, sql="SELECT `name`, SUM(`age`) AS `pushed_sum`, COUNT(`age`) AS `pushed_count` FROM `t2` AS `t2` GROUP BY `name`")

两阶段聚合的优化能大大减少数据传输量、提高执行效率。

总的来说,大部分场景做聚合的时候都倾向于选择HashAgg,只要当以下场景下才适合选择SortAgg做聚合:

  1. 数据比较多,内存严重不足。
  2. 聚合算子的输入已经按照Group By 列做好排序,这样做SortAgg就不需要额外排序,执行效率会更高。
  3. 当数据有严重倾斜,导致HashAgg执行效率不高,优先使用SortAgg
相关文章
|
存储 Kubernetes 应用服务中间件
【K8S系列】深入解析无状态服务
【K8S系列】深入解析无状态服务
801 2
|
10月前
|
存储 运维 安全
盘古分布式存储系统的稳定性实践
本文介绍了阿里云飞天盘古分布式存储系统的稳定性实践。盘古作为阿里云的核心组件,支撑了阿里巴巴集团的众多业务,确保数据高可靠性、系统高可用性和安全生产运维是其关键目标。文章详细探讨了数据不丢不错、系统高可用性的实现方法,以及通过故障演练、自动化发布和健康检查等手段保障生产安全。总结指出,稳定性是一项系统工程,需要持续迭代演进,盘古经过十年以上的线上锤炼,积累了丰富的实践经验。
753 7
|
存储 分布式计算 流计算
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
本文介绍了阿里云开源大数据团队在实时计算领域的最新成果——向量化流计算引擎Flash。文章主要内容包括:Apache Flink 成为业界流计算标准、Flash 核心技术解读、性能测试数据以及在阿里巴巴集团的落地效果。Flash 是一款完全兼容 Apache Flink 的新一代流计算引擎,通过向量化技术和 C++ 实现,大幅提升了性能和成本效益。
3494 73
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
|
API 图形学 Swift
【Swift开发专栏】Swift与Core Graphics框架
【4月更文挑战第30天】本文介绍了Swift如何与Apple的Core Graphics框架结合,用于高性能的图形渲染和用户界面设计。Core Graphics提供底层绘图接口,包括图形上下文、路径、颜色空间、渐变和阴影等概念。在Swift中,开发者可直接调用Core Graphics函数,创建图形上下文、设置绘图属性、绘制图形和处理图像。文章还展示了如何实现渐变填充、阴影效果及自定义绘图代码,帮助开发者利用Swift创造复杂的图形和动画。通过掌握这些技能,开发者能为移动应用打造更具吸引力的视觉体验。
326 1
|
机器学习/深度学习 数据采集 算法
推荐引擎离线算法与在线算法的探索与实践
推荐引擎是现代互联网产品中至关重要的组成部分。离线算法和在线算法分别负责处理大量数据的预处理和模型训练,以及快速响应用户的实时请求。通过合理的架构设计和算法选择,可以构建出高效且个性化的推荐系统,从而提升用户体验,增加用户满意度和留存率。未来,随着技术的发展,推荐引擎将更加智能化和个性化,为用户提供更加精准的服务。
|
安全 Linux Shell
SSH服务器拒绝密码登录的解决方法
SSH服务器拒绝密码登录的解决方法
3770 1
|
编解码 Unix Linux
【Linux C/C++ 延时(延迟)函数比较】介绍Linux系统中常用的延时函数sleep、usleep、nanosleep、select和std::sleep_for()的区别和使用场景
【Linux C/C++ 延时(延迟)函数比较】介绍Linux系统中常用的延时函数sleep、usleep、nanosleep、select和std::sleep_for()的区别和使用场景
4453 1
|
算法 网络协议 Linux
Linux模块文件编译到内核与独立编译成.ko文件的方法
Linux模块文件编译到内核与独立编译成.ko文件的方法
4351 0
|
Linux Shell
Linux系统调试篇——stty修改串口波特率
Linux系统调试篇——stty修改串口波特率