跳转到主要内容
ClickHouse 处理查询的速度极快,但查询的执行过程并不简单。下面我们来了解一下 SELECT 查询是如何执行的。为了便于说明,先在 ClickHouse 的一个表中添加一些数据:
现在 ClickHouse 中已经有了一些数据,我们可以运行一些查询并了解它们的执行过程。查询执行会被拆分为多个步骤。查询执行的每个步骤都可以通过相应的 EXPLAIN 查询进行分析和故障排查。下图概括了这些步骤: 下面我们来看这些环节在查询执行过程中是如何工作的。我们将选取几个查询,然后使用 EXPLAIN 语句来分析它们。

解析器

解析器的目标是将查询文本转换为 AST (抽象语法树) 。可以用 EXPLAIN AST 直观地展示这一步骤:
输出结果是一棵抽象语法树,可视化效果如下所示: 每个节点都有对应的子节点,整棵树表示查询的整体结构。这是一种有助于处理查询的逻辑结构。从终端用户的角度来看 (除非你对查询执行感兴趣) ,它的实用性并不高;这个工具主要供开发者使用。

Analyzer

ClickHouse 目前有两套 Analyzer 架构。你可以通过设置 enable_analyzer=0 使用旧架构。新架构默认启用。由于旧架构会在新 analyzer 正式可用后被弃用,这里我们只介绍新架构。
新架构应当能提供一个更好的框架,帮助提升 ClickHouse 的性能。不过,由于它是查询处理流程中的基础组件,也可能会对某些查询产生负面影响,并且存在一些已知的不兼容问题。你可以在查询级别或用户级别修改 enable_analyzer 设置,切换回旧 analyzer。
analyzer 是查询执行中的一个重要步骤。它接收 AST,并将其转换为查询树。与 AST 相比,查询树的主要优势在于其中许多组件都已完成解析,例如具体使用的存储。我们还能够确定应从哪张表读取数据,别名也会被解析,并且查询树也知道所使用的各种数据类型。有了这些优势,analyzer 就可以应用优化。这些优化是通过“passes”实现的。每个 pass 都会寻找不同的优化机会。你可以在这里查看所有 passes,下面我们结合之前的查询来看实际效果:
对比这两次执行结果,可以看到别名和投影的解析过程。

规划器

规划器接收查询树,并据此生成查询计划。查询树描述的是我们希望对某个特定查询执行什么操作,而查询计划说明的是这些操作将如何执行。进一步的优化也会在生成查询计划的过程中完成。你可以使用 EXPLAIN PLANEXPLAIN 来查看查询计划 (EXPLAIN 会执行 EXPLAIN PLAN) 。
虽然这已经给了我们一些信息,但我们还能获取更多。例如,我们可能还想知道需要在其上创建投影的列名。你可以在查询中添加请求头:
现在你已经知道了最后一个 Projection 需要创建哪些列名 (minimum_datemaximum_datepercentage) ,但你可能还想查看所有待执行操作的详细信息。你可以通过设置 actions=1 来实现。
现在,你可以看到所有正在使用的输入、函数、别名和数据类型。你还可以在这里查看规划器将应用的部分优化。

查询管道

查询管道由查询计划生成。查询管道与查询计划非常相似,区别在于它不是树形结构,而是图结构。它展示了 ClickHouse 将如何执行查询以及将使用哪些资源。分析查询管道对于定位输入/输出方面的性能瓶颈非常有帮助。下面以之前的查询为例,来看看查询管道的执行情况:
括号内是查询计划步骤,旁边是处理器。这些信息非常有用,但由于这本质上是一个图结构,如果能将其可视化会更加直观。我们可以将 graph 设置为 1,并将输出格式指定为 TSV:
您可以将此输出复制并粘贴到此处,从而生成以下图形: 白色矩形对应管道节点,灰色矩形对应查询计划步骤,x 后跟的数字表示当前使用的输入/输出数量。如果不希望以紧凑形式显示,可以添加 compact=0
为什么 ClickHouse 没有使用多个线程从表中读取数据?我们来尝试向表中添加更多数据:
现在再次运行我们的 EXPLAIN 查询:
因此,执行器认为无需将这些操作并行化,因为数据量还不够大。增加更多行后,执行器便决定改用多个线程,如图所示。

执行器

最后,查询执行的最终一步由执行器完成。它会接收查询管道并执行。根据执行的是 SELECTINSERT 还是 INSERT SELECT,所使用的执行器类型也会不同。
最后修改于 2026年7月2日