bigywater.tech
ClickHouse 内部原理 · 17

PREWHERE:为什么它可能比 WHERE 更快

真实对比默认设置下WHERE和显式PREWHERE的执行计划,发现两者完全相同,再手动关掉自动优化验证背后的真实原因。

完全相同
默认设置下WHERE和PREWHERE的执行计划真实一字不差
默认已开启
optimize_move_to_prewhere真实默认值是1

做的事:真实对比WHERE和PREWHERE的执行计划

代码CREATE TABLE demo.pw2 (k UInt64, flag UInt8, payload String) ENGINE=MergeTree ORDER BY k; INSERT INTO demo.pw2 SELECT number, number<10000, repeat('x',500) FROM numbers(2000000); EXPLAIN actions=1 SELECT sum(length(payload)) FROM demo.pw2 WHERE flag=1; EXPLAIN actions=1 SELECT sum(length(payload)) FROM demo.pw2 PREWHERE flag=1;

验证:默认情况下,WHERE的执行计划真实跟PREWHERE一模一样

实测 · 本机执行输出(两条EXPLAIN,一条WHERE一条PREWHERE,默认设置)
-- 普通WHERE,什么SETTINGS都没加:
EXPLAIN actions=1 SELECT sum(length(payload)) FROM demo.pw2 WHERE flag=1;
→ ReadFromMergeTree (demo.pw2)
     Output: payload.size
     Prewhere filter
     Prewhere filter column:  flag = 1   ← 注意:WHERE被自动改写成了Prewhere filter

-- 显式PREWHERE:
EXPLAIN actions=1 SELECT sum(length(payload)) FROM demo.pw2 PREWHERE flag=1;
→ ReadFromMergeTree (demo.pw2)
     Output: payload.size
     Prewhere filter
     Prewhere filter column:  flag = 1   ← 真实完全一样的计划

SELECT value, changed FROM system.settings WHERE name='optimize_move_to_prewhere';
→ 1   0   ← 真实默认值就是1(开启),不用手动设置

两条EXPLAIN的输出真实一字不差。原因就在最后一条查询里:optimize_move_to_prewhere这个设置真实默认值是1(changed=0说明这是原始默认值,不是本文改过的),意味着ClickHouse的查询优化器会自动分析WHERE子句里的每个条件,把符合条件的(比如条件只依赖单一列、且这一列本身不算太重)自动提升成内部的PREWHERE步骤,完全不需要手写。

验证:手动关掉这个优化,执行计划真实变了

实测 · 本机执行输出(SETTINGS optimize_move_to_prewhere=0)
EXPLAIN actions=1 SELECT sum(length(payload)) FROM demo.pw2
WHERE flag=1 SETTINGS optimize_move_to_prewhere=0;
→ Aggregating
     └──Filter ((WHERE + Change column names to column identifiers))
        │  Filter column: flag = 1
        └──ReadFromMergeTree (demo.pw2)
              Output: payload.size, flag      ← 真实变成了普通Filter,不再是Prewhere filter
这组对比真实说明了一件在ClickHouse较新版本里容易被老资料误导的事:PREWHERE曾经是一个需要开发者手写才能拿到性能收益的"魔法关键字",但ClickHouse的查询优化器早已把这个决策自动化了——optimize_move_to_prewhere默认开启之后,普通的WHERE子句在大多数简单场景下真实会被自动改写成跟手写PREWHERE完全相同的执行计划,没必要每次都手动加这个关键字。PREWHERE在今天真正还有价值的场景,是当WHERE子句里有多个过滤条件、而优化器自动选择的"先过滤哪一列"顺序不是最优的时候——比如你明确知道某个便宜的整数列过滤掉99%的行,而另一个字符串列的正则匹配很贵,这时候手写PREWHERE cheap_col=x AND expensive_col LIKE '%y%'(PREWHERE支持多个用AND连接的条件,按书写顺序真实依次执行)能够比依赖优化器自动决策更可控。对于本文这种单一过滤条件的场景,写不写PREWHERE,在默认配置下真实没有区别。

分步演示:从默认WHERE到手动关闭自动提升

每一步都是本文上面用真实EXPLAIN actions=1跑出来的原始结果。

WHERE真实被自动改写成PREWHERE,不用手写
这一步新验证的执行计划

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 EXPLAIN actions=1
    • 真实的 system.settings(optimize_move_to_prewhere 默认值确认)
    京ICP备2026067502号-1京公网安备11010502063487号