跳转到主要内容
本文介绍惰性物化的工作机制,以及它在 ClickHouse 更广泛的 I/O 优化栈中的作用。 本文还通过一个真实场景示例,展示惰性物化如何提升查询性能。
自 25.4 版本起可用惰性物化于 ClickHouse 25.4 版本中引入,并默认启用。

概述

多年来,ClickHouse 引入了一系列分层优化,以大幅减少 I/O。 这些技术构成了其高性能和高效率的基础: 尽管上述 I/O 优化可以显著减少数据读取量,但它们仍然假定:在执行排序、聚合或 LIMIT 等操作之前,必须先加载所有通过 WHERE 子句的行所对应的全部列。但如果某些列要到后续阶段才需要,或者某些数据虽然通过了 WHERE 子句,实际上却根本不会被用到,又该怎么办? 这正是 惰性物化 发挥作用的地方。它是一种正交增强,补全了整个 I/O 优化栈:
  • 索引结合 PREWHERE,可确保只处理匹配 WHERE 子句中列过滤条件的行。
  • 惰性物化 在此基础上,进一步将列读取推迟到查询执行计划实际需要时才进行。 即使在过滤之后,也只会立即加载下一步操作所需的列——例如排序所需的列。 其他列会延后读取,并且由于 LIMIT 的存在,通常只会部分读取,仅读取足以生成最终结果的数据。 这使得 惰性物化 对 Top N 查询尤其有效,因为最终结果可能只需要从某些列 (而这些列往往很大) 中取出少量几行。

一个完整示例

我们强烈建议阅读博客文章”ClickHouse gets lazier (and faster): Introducing lazy materialization”,以深入了解 惰性物化。下面的示例摘自上述博文,并在此重现,用于展示 ClickHouse 查询如何借助 惰性物化 将耗时从 219 秒缩短到仅 139 毫秒 (提速 1576×) 。 要从索引和 PREWHERE 中受益,查询需要具备过滤条件:对于索引,过滤条件需作用于主键列;对于 PREWHERE,则可以作用于任意列。 惰性物化 可以自然地叠加在这些优化之上,但与前面提到的其他优化不同,它甚至还能加速完全没有列过滤条件的查询。 来看下面这个示例查询:它会找出 helpful votes 数量最多的 Amazon 评论,不考虑日期、产品、评分或验证状态,并返回前 3 条评论及其 title、headline 和全文。 首先,在禁用 惰性物化 (使用 query_plan_optimize_lazy_materialization) 的情况下运行该查询 (文件系统缓存处于冷状态) :
Query
Response
接着,再次运行该查询 (同样是在文件系统缓存未预热的情况下) ,但这次启用了延迟物化:
Query
一般情况下,你无需显式设置 query_plan_optimize_lazy_materialization = true,也能享受到惰性物化带来的好处。 该选项默认已启用。
Response
比较关闭和启用惰性物化时的性能差异:

如何在查询执行计划中确认惰性物化

你可以使用 EXPLAIN 子句查看该查询的逻辑执行计划,以观察前一个查询中惰性物化的使用情况:
您可以自下而上查看该算子计划,并看到 ClickHouse 会在排序和限制之后,才读取这三个较大的 String 列。
最后修改于 2026年6月10日