bigywater.tech
ClickHouse 内部原理 · 8

ORDER BY、Primary Key 和 Partition Key 的区别

建一张30万行、按月分区、按region+uid排序的表,用3种不同的WHERE条件对比read_rows,看ORDER BY前缀、非索引列、分区裁剪各自的效果。

24577<75226
命中ORDER BY前缀时读取量比匹配数还少
300000
命中非索引列时的全表扫描行数
100548
分区裁剪后只扫描单个分区大小

做的事:一张30万行的订单表,3种建表要素同时生效

按月分区,按region+uid排序,插入横跨3个月的30万行随机数据。

建表CREATE TABLE demo.orders (d Date, region String, uid UInt64, amount Float64) ENGINE=MergeTree PARTITION BY toYYYYMM(d) ORDER BY (region, uid) 插入30万行,日期随机分布在2026-01-15 / 2026-02-20 / 2026-03-10 SELECT partition, count() parts, sum(rows) FROM system.parts WHERE table='orders' → 202601 1 part 99432行 → 202602 1 part 100548行 → 202603 1 part 100020行

这张表同时用到了3个概念:PARTITION BY 决定数据物理上分成几组文件(这里是3个月各一组),ORDER BY 决定每个分区内部数据按什么顺序排列并建立稀疏索引。它们各自解决不同的问题,测一遍才知道差在哪。

验证:命中 ORDER BY 前缀 vs 命中非索引列

同一张表,一条查询过滤 ORDER BY 的第一列(region),一条过滤不在 ORDER BY 里的列(amount),对比 system.query_log 里的 read_rows。

结果SELECT count() FROM demo.orders WHERE region='bj' → 匹配75226行,read_rows只有24577 SELECT count() FROM demo.orders WHERE amount > 999 → 匹配313行,read_rows是300000(全表扫描)

过滤 amount 这种不在 ORDER BY 里的列,ClickHouse 没有任何索引信息可用,把30万行全部扫了一遍才能找出符合条件的313行。过滤 region(ORDER BY 的第一列)则完全不同:因为数据在每个分区内部是按 region 物理排序的,同一个 region 的行必然连续挨在一起,稀疏索引可以直接定位到这段连续区间的起止位置,读取量(24577)甚至比最终匹配的行数(75226)还要少——因为大部分落在区间内部的行,ClickHouse 靠索引直接确认"肯定符合条件",不需要真的去读它们,只有区间边界那一小部分颗粒需要读取来确认精确边界。

这就是 ORDER BY 存在的意义:它决定了数据在磁盘上物理排列的顺序,从而决定了哪些过滤条件能享受到"稀疏索引直接跳过大段数据"的红利——只有过滤条件命中 ORDER BY 的前缀列时才有效。

验证:分区裁剪单独生效,跟 ORDER BY 无关

再测一条过滤日期的查询,日期只用于 PARTITION BY,不在 ORDER BY 里。

结果SELECT count() FROM demo.orders WHERE d = '2026-02-20' → read_rows正好是100548,等于2月这一个分区的全部行数

这条查询只读了 100548 行,一行不多一行不少,正好是 202602 这个分区的总行数——ClickHouse 靠 PARTITION BY toYYYYMM(d) 直接跳过了 1 月和 3 月这两个分区(总共约20万行),但因为 d 不在 ORDER BY (region, uid) 里,进了 2 月这个分区之后就没有更细的索引可用了,只能把这个分区内部100548行全部扫一遍。

三者的分工很清楚:PARTITION BY 决定能不能整个跳过一批文件(分区级别,粗粒度);ORDER BY 决定分区内部数据的物理顺序,从而决定稀疏索引能不能在分区内部继续细粒度跳过;不在两者之中的列,一律没有索引可用,只能老实扫描命中的那部分数据。

分步演示:ORDER BY前缀、非索引列、PARTITION BY,读取量对比

每一步都是本文上面查询system.query_log得到的原始read_rows。

同一张表,3种过滤条件读取量差异巨大
这一步新验证的过滤条件

    本文用到的工具

    • 本机 ClickHouse 26.9.1,30万行 MergeTree 表,PARTITION BY + ORDER BY 复合建表
    • system.parts:核对每个分区的part数与行数
    • system.query_log:对比不同WHERE条件下的read_rows
    京ICP备2026067502号-1京公网安备11010502063487号