本文讲述ClickHouse中SQL编译阶段的核心问题之一,如何进行表达式计算。
一、ActionsChain解决什么问题?
SQL的执行是按照执行计划中的步骤有序执行的,比如以下SQL的各个步骤的执行顺序为:from -> where -> select -> order by。每个步骤都会有一些表达式,每个下游步骤产生的表达式对应的数据会作为上游步骤的输入,那么下游步骤需要输出那些表达式给上游呢?其中如何进行优化呢?
ActionsChain被设计用来解决这个问题。
在一个SQL中不仅select中会生成表达式,其它步骤中也会生成表达式,比如:下面的SQL中where生成了“b > 1”、“b + c”、“b + c > 2”和“b > 1 and b + c > 2”四个表达式。
query_1: select a + 1, a + 2, b > 1, b + c from t where b > 1 and b + c > 2 order by a + 1;
在以上SQL的执行过程中有几个问题:
1 where向select输出数据的时候是输出a,b,c三个表达式,还是它生成的四个表达式,还是所有?
2 where向select输出数据的时候如何尽量少的输出数据,比如:在select中有2个与a列相关的表达式,where是否需要输出2个a列?
3 where是否需要向select输出c列?
ActionsChain被设计用来统筹计算整个查询计划中所有步骤的表达式计算,并对每个步骤的表达式输出进行优化,优化的目标与原则是尽量少的向上输出数据。在下面对ActionsChain的分析中将逐一解答以上问题。
PS: 可以通过Explain actions=1 sql
的方式查看执行计划各个步骤间的action情况。
二、 ActionsChain结构示意
ActionsChain是由ActionsDAG组成的DAG结构,每个步骤的输出是下一个步骤的输入吗,每个ActionsDAG是表达式列表转换成的有向无环图。对于from步骤由于是起始步骤没有输入,它的输出是整个SQL用到的所有原始列的列表,实际上我们常常忽略from步骤,直接从where开始计算。以下简单查询对应的ActionsChain如下所示。
query_2: select a, b + 1 from t wher a > 0
三、ActionsChain构建流程
上图中展示了一个最终状态的ActionsChain结构,这里介绍ActionsChain构建过程,其主要流程包含构建和裁剪两个步骤,其中构建是指生成ActionsChain结构,在生成的过程中会保留所有的表达式并输出给下游,这样生成的ActionsChain包含了很多不必要的步骤,所以需要进行裁剪,裁剪实质把整个下游不需要的表达式从输出列表里删除。从而达到尽量少的向下游输出数据的目的。
query_3: select a+1, b from t where a > 1;
可以看到where中的所有节点全部输出给了select,总共有a, b, greater(a, 1)三个列的数据需要输出给select,但是其中的greater(a, 1)是不需要,所以需要裁剪,裁剪后的ActionsChain如下:
四、表达式复用
1. 如果下游已经计算过的表达式上游可以直接复用表达式的结果,它对应的ActionsChain如下:
query_4: select a+1, b from t where a + 1 > 1
对于表达式”a + 1″ 从where处计算得到,到后来的projection阶段不在重复计算,而是可以从下游直接获取到,从而查提升询效率。
2. 如果上游需要多个相同的表达式,那么下游只需要输出一个。
query_5: select a+1, a+1 from t where a+1 > 1;
ActionsChain统筹整个查询中各个步骤的表达式计算,本着向上输出最少数据和尽量输出计算度高的表达式的原则,尽量减少了数据传输和计算,对ClickHouse性能提升有很大帮助。
本作品采用 知识共享署名 4.0 国际许可协议 进行许可, 转载时请注明原文链接。