Snowflake MigrationClickHouse Workshops
规划练习表

练习表 2:Sort key 设计(ORDER BY)

从查询负载出发为每张 NYC 出租车表推导 ORDER BY,每个答案都有即时反馈。

预计耗时: 20–25 分钟 参考资料: MergeTree 引擎,ORDER BY 设计一节

概念

ClickHouse 的 ORDER BY 子句不是装饰。它定义了主索引,一个 稀疏的、块级别的索引,让 ClickHouse 在计算 WHERE 子句时能跳过无关的数据块。它还决定了 part 内部数据的物理排序次序,而这会影响压缩率。

错误的 ORDER BY = 慢查询 + 浪费存储。 以 UUID 开头的 ORDER BY 意味着任何分析查询都无法 跳过数据块(UUID 是随机的;不存在可排序的前缀)。 以日期开头的 ORDER BY 意味着按日期过滤的查询能跳过表中的大部分数据。

ORDER BY 设计的三条规则

规则 1:从查询过滤条件推导,而不是从源 schema 推导。 看你最频繁的那些查询中的 WHERE、 GROUP BY 和 JOIN 列。被过滤最多的 列大概应该出现在 ORDER BY 中(前提是基数不要太高)。 源表的 primary key(如果有的话)通常无关紧要。

规则 2:低基数在前,高基数在后。 ClickHouse 的主索引 每约 8192 行(一个 granule)有一个条目。低基数列(例如 toStartOfMonth(date) = 4 年内约 48 个不同值)把大量行聚集在一起, 索引可以跳过整个 granule。高基数列(例如 trip_id = 5000 万个 不同值)每行都唯一,把它放在最前面意味着索引什么都跳不了。这个次序是默认做法,不是对规则 1 的 推翻:一个被查询按范围过滤、能排除掉大部分行的列,仍然可以领先于一个只会被按等值过滤的 更低基数列。

规则 3:对 ReplacingMergeTree,以唯一行标识符结尾。 去重键是完整的 ORDER BY 元组。如果 ORDER BY 中缺少 trip_id, 两次拥有相同 pickup_at、且没有更多列区分的不同行程就会被当成 重复行。把 trip_id 放在最后,既保证唯一性又不损害索引性能。

练习:查询负载分析

在设计 sort key 之前,先弄清楚这些查询实际过滤的是哪些列。 NYC 出租车实验有 7 个代表性查询;对每一个,挑出能排除最多行的 过滤列。

练习:基数估算

对每个候选 ORDER BY 列,估算它在这份 4 年、5000 万行的 数据集上的基数。下表大部分是参考数据,两个待填单元格是 pickup_at 自己的估算不同值数量和基数等级。四年大约是 1.26 亿 秒(而分钟数只有约 210 万),生产者按 墙上时钟为每次行程打时间戳,而 PICKUP_AT 存储为 DateTime64(3, 'UTC'),先算出 5000 万次行程 能占据其中多少个槽位,再去选区间。

练习:sort key 设计

利用你的查询负载分析和基数估算,为 trips_raw、fact_trips 和 agg_hourly_zone_trips 各提出一个 ORDER BY。提醒:

  • 低基数在前 → 能跳过最多数据块。
  • 出现在多个查询的 WHERE/GROUP BY 中的列 → 把它们包含进来。
  • 对 ReplacingMergeTree 表 → 以唯一行标识符结尾。
  • 不要包含从不用于过滤的列。

每张表都填完之后,再作答推理与反思问题。

Loading worksheet...

转录到 migration-plan.md

填完这份练习表之后,把你的 ORDER BY 决策抄到 migration-plan.md 的第 4 节,并勾选:

- [ ] Sort key design: completed

本页内容

Track your progress?

Optional. We email a link to confirm your address; progress records once you open it.

Please use your work email address, not a personal one.

Progress tracking also requires accepting the current Terms of Service in Privacy settings.

ZH