理解分析器的查询执行
ClickHouse 查询执行的速度极快,但查询的执行并不是一个简单的故事。让我们尝试理解一个 SELECT
查询是如何执行的。为了说明这一点,让我们在 ClickHouse 的表中添加一些数据:
现在我们在 ClickHouse 中有了一些数据,我们想运行一些查询并理解它们的执行。查询的执行被分解为许多个步骤。可以使用相应的 EXPLAIN
查询分析和排除故障查询执行的每个步骤。以下图表总结了这些步骤:

让我们看看查询执行过程中的每个实体。我们将选取几个查询,然后使用 EXPLAIN
语句进行检查。
解析器
解析器的目标是将查询文本转换为抽象语法树 (AST)。这个步骤可以使用 EXPLAIN AST
进行可视化:
输出是一个可以被可视化的抽象语法树,如下所示:

每个节点都有相应的子节点,整个树代表了查询的整体结构。这是一个逻辑结构,有助于处理查询。从最终用户的角度来看(除非对查询执行感兴趣),这不是非常有用;这个工具主要由开发人员使用。
分析器
ClickHouse 当前有两种分析器架构。您可以通过设置 enable_analyzer=0
来使用旧架构。新架构默认启用。我们在此仅描述新架构,因为旧架构在新分析器普遍可用后将被弃用。
新架构应该为我们提供一个更好的框架,以提高 ClickHouse 的性能。然而,由于它是查询处理步骤的一个基本组件,它也可能对某些查询产生负面影响,并且存在 已知的不兼容性。您可以通过更改查询或用户级别的 enable_analyzer
设置来回退到旧分析器。
分析器是查询执行的重要步骤。它接受抽象语法树并将其转换为查询树。查询树相对于抽象语法树的主要好处在于许多组件将被解析,例如存储。我们还知道从哪个表中读取,别名也已解析,树中知道使用的不同数据类型。凭借这些好处,分析器可以应用优化。这些优化的工作方式是通过“遍历”。每个遍历将寻找不同的优化。您可以在 这里 查看所有遍历,让我们在实践中看看我们之前的查询:
在两次执行之间,您可以看到别名和投影的解析。
规划器
规划器接受查询树并从中构建查询计划。查询树告诉我们希望对特定查询做什么,而查询计划则告诉我们将如何做。这将作为查询计划的一部分进行额外优化。您可以使用 EXPLAIN PLAN
或 EXPLAIN
查看查询计划(EXPLAIN
将执行 EXPLAIN PLAN
)。
尽管这给我们提供了一些信息,但我们仍然可以获得更多。例如,也许我们想知道需要进行投影的列的名称。您可以向查询添加头信息:
所以现在您知道需要为最后一个投影创建的列名(minimum_date
、maximum_date
和 percentage
),但您可能还想了解所有需要执行的操作的详细信息。您可以通过设置 actions=1
来做到这一点。
现在您可以看到所有使用的输入、函数、别名和数据类型。您可以在 这里 查看规划器将应用的一些优化。
查询管道
查询管道是从查询计划生成的。查询管道与查询计划非常相似,区别在于它不是一棵树而是一个图。它突出了 ClickHouse 将如何执行查询以及将使用哪些资源。分析查询管道非常有用,可以查看输入/输出方面的瓶颈。让我们拿前面的查询来查看查询管道的执行:
括号内是查询计划步骤,旁边是处理器。这是很好的信息,但因为这是一个图,如果能以图的形式可视化会更好。我们有一个设置 graph
可以设置为 1,并指定输出格式为 TSV:
然后您可以复制该输出并粘贴到 这里,将生成以下图:

白色矩形对应于管道节点,灰色矩形对应于查询计划步骤,x
后跟一个数字对应于正在使用的输入/输出数量。如果您不想以紧凑的形式看到它们,您始终可以添加 compact=0
:

为什么 ClickHouse 不使用多个线程读取表?让我们尝试向表中添加更多数据:
现在让我们再次运行我们的 EXPLAIN
查询:

所以执行器决定不对操作进行并行化,因为数据量不够大。通过添加更多行,执行器随后决定如图所示使用多个线程。
执行器
最后,查询执行的最后一步由执行器完成。它将接受查询管道并执行它。根据您是进行 SELECT
、INSERT
还是 INSERT SELECT
,有不同类型的执行器。