揭秘字节跳动解决ClickHouse复杂查询问题的技术方案( 五 )


5. 诊断和分析
引入复杂查询的多Stage 执行模型后,SQL的执行模式变得复杂了 。特别是当用户查询一些非常复杂的查询,几百行的sql生成的stage会非常多,把stage都看一遍并理解sql的含义要花比较长的时间 。题外话:我们很早之前就完整的跑通了所有的tpcds query,这里面就有一些sql可能会产生几十个 stage 。那么在这种情况下,如何定位 SQL 的瓶颈并加以优化是一个难题 。
我们做了如下两点优化:
首先,最常见的做法是增加各类完善的metrics,包括整个Query的执行时间和不同Stage的执行时间、IO数据量、算子处理数据和执行情况、算子 metrics 和profile event等 。
其次,我们记录了反压信息和上下游队列长度,以此来推断 stage 执行情况和瓶颈 。
坦率地说,SQL 场景包括万象,很多非常复杂的场景目前还是需要对引擎比较熟悉的同学才能诊断和分析SQL才能给出优化建议 。在不断积累经验的过程中,我们希望通过能够不断完善 metrics 和分析路径,不断减轻oncall的负担,并且在某些场景下可以更智能地给出优化提示,这对于使用同学来说也是有好处的 。
04
效果及展望
1. 复杂查询效果
根据上面的执行模型的三个缺点,分别测试如下三个场景:
①第二阶段的计算比较复杂
②Hash Join 右表为大表
③多表 Join
以SSB 1T数据作为数据集,集群包含8个节点 。
2. 第二阶段的计算比较复杂
这个case SQL 如下图所示:

揭秘字节跳动解决ClickHouse复杂查询问题的技术方案

文章插图
 
uniqExact是count distinct的默认算法,采用hash table进行数据去重 。使用复杂查询后,query 执行时间从 8.514s=>2.198s,第二阶段 agg uniqExact 算子的合并原本由 coordinator单点合并,现在通过按照group by key shuffle 后可以由多个节点并行完成 。因此通过shuffle减轻了coordinator的merge agg 压力 。
3. Hash Join 右表为大表
这个 case 演示了右表是一个大表的场景,由于 ClickHouse 对多表的优化做的还不是很到位 。这里采用子查询来下推过滤的条件 。
揭秘字节跳动解决ClickHouse复杂查询问题的技术方案

文章插图
 
在这个case中,采用复杂查询模式后,query 执行时间从17.210=>1.749s 。lineorder 是一张大表,通过shuffle可以将大表数据按照join key shuffle到每个worker节点,减少了右表构建的压力 。
4. 多表 Join
这个 case 是一个 5 表 join 的 case 。
揭秘字节跳动解决ClickHouse复杂查询问题的技术方案

文章插图
 
开启复杂查询模式后,query 执行时间从8.583s=>4.464s,所有的右表可同时开始数据读取和构建 。为了和现有模式对比,针对复杂查询没有开启 runtime filter,开启 runtime filter后效果会更快 。
这里还要重点说一下,今天的分享主要是从执行模式上讲解如何支持复杂查询 。实际上,优化器对于复杂查询的性能提升也非常大 。通过一些rbo的规则,比如常见的谓词下推、相关子查询处理等 。实际上这里的优化规则非常多,可以极大的提升 SQL 的执行效率 。上面的 SQL 其实原本比较简单,5 表 join 和一些维表的过滤条件,这里写成子查询是为了在 ClickHouse 现有模式下右表过滤条件更好下推 。其实对于我们来说,在复杂查询的模式下,由于有优化器的存在,用户不用写的这么复杂,优化器会自动完成下推和rbo优化 。
上面是一些规则的优化,实际上在复杂查询中,cbo 的优化也有很大作用 。举一个例子,在 ClickHouse 中,相同的两个表,大表 join 小表的性能比小表 join 大表要好很多 。前一个效果 2 中如果把表顺序调整一下会快很多;另外,选用哪一种 join 的实现对 join 性能影响比较大,如果满足 join key 分布,colcate join 比 shuffle join 来说完全减少了数据的 shuffle 。多表 join 中,join 的顺序和 join 的实现方式对执行的时长影响会比 2 表 join 影响更大 。借助数据的统计信息,通过一些 cbo 优化,可以得到一个比较优的执行模式 。
有了优化器,业务同学可以按照业务逻辑来写任何的 SQL,引擎自动计算出相对最优的 SQL 计划并执行,加速查询的执行 。


推荐阅读