ClickHouse:排序键、分区与版本
前置:表DDL、排序与逻辑事件键。events/latest两张表、六行CSV及版本例见完整工程,重点对照00-schema.sql和20-versions.sql。适用:单节点ClickHouse 24.8 LTS基础语法;资料核对2026-10-17。示例未执行,本轮仅静态解析和文本审阅,不连接数据库、启动服务或调用产品接口。
ClickHouse 排序键
ClickHouse 排序键决定每个part内的数据排列,主键通常沿用排序键并建立稀疏索引,它不是唯一约束。相同ORDER BY值可以被重复插入,普通MergeTree不会因此拒绝或删除记录。本例ORDER BY(tenant_id,event_time,event_id)服务租户时间范围查询,event_id放末尾并不产生业务去重能力。
稀疏索引指向granule范围,不是为每行建立树节点。设计键要从常见筛选和数据分布出发:租户等值加时间范围可利用前缀;只按event_id找单行通常不适合当前键。显式PRIMARY KEY若不同于ORDER BY,需要是排序键前缀;索引省内存与检索选择性之间存在取舍。
题目:把ORDER BY改成event_id是否所有查询更快?解答:它可能改善ID范围读取,却削弱租户时间访问的聚集性。应以真实查询集合和读取工作量比较,而非按“ID就是主键”的交易库习惯决定。DDL里的ORDER BY也不保证SELECT输出顺序,输出仍显式排序。
ClickHouse 分区设计
ClickHouse 分区设计主要服务生命周期管理与粗粒度裁剪,分区不是索引,不能替代ORDER BY的范围定位。本例按toYYYYMM(event_time)分月,便于按月管理数据;条件可推导月份时有机会跳过其他分区。同一个分区里仍需按索引和列过滤读取,不能保证一个月只有一个part。
不同分区的parts不会后台合并,因此按租户甚至唯一事件分区会造成大量小分区和parts,增加元数据与合并压力。月粒度也不是普遍最优:数据体量、保留周期与典型维护操作决定是否分区及粒度。按月查并不自动要求分月;小表可完全不分区。
实验数据跨202601与202602两个分区,system.parts可显示每月active part数量和行数,但具体数量取决于导入和合并时机。DROP PARTITION是删除该分区数据的维护动作,不是撤销事务,执行前需核对范围与备份;本包不提供自动删除历史分区脚本。
ClickHouse ReplacingMergeTree
ClickHouse ReplacingMergeTree按完整排序键识别同一逻辑行,并在合并时保留最高版本;后台合并没有完成时间保证,FINAL可在查询时应用替换规则。它不是写入时唯一约束,也不是跨分片全局去重。版本必须由业务保证对同一键单调且冲突可判定,不能依赖相同版本不同内容的偶然插入顺序。
latest表使用ORDER BY(tenant_id,event_id),version不在排序键内,避免每个版本变成不同键。写入(7,1,1,10.00)、(7,1,2,12.00)、(7,2,1,5.00),FINAL结果是两个逻辑事件、金额合计17.00。不带FINAL的行数可随合并时机变化,不能固定断言三行或两行。
本表故意不分区,避免同键版本落在不同分区的问题。若增加分区,应保证同键所有版本映射相同分区;后台不能跨分区替换,FINAL跨分区行为还受do_not_merge_across_partitions_select_final等设置影响。FINAL消耗查询资源且不改写磁盘;OPTIMIZE FINAL强制合并代价可能很高,不能作为每次查询前的补丁。
官方依据与范围
中文与英文MergeTree、ReplacingMergeTree对照核对;翻译或当前文档包含的新功能不自动适用于24.8。本课没有运行该版本,只有静态证据。旧文ClickHouse概述保留原URL与正文,旧容量描述不作为本课性能结论。
继续阅读
- 列式模型与SQL
- 完整事件项目
- 诊断与恢复