ClickHouse中分区键是否需加入PRIMARY KEY或ORDER BY列表?
ClickHouse分区表排序键与主键设计建议
核心结论
不需要将分区键logging_day加入ORDER BY或PRIMARY KEY列表,第一种写法的查询性能更优,第二种写法因冗余字段反而会带来微小的性能损耗。
具体分析
1. 分区键的核心作用
ClickHouse的PARTITION BY logging_day已经将数据按日期做了物理隔离,当执行WHERE logging_day = '2024-04-20'这类查询时,引擎会直接定位到该日期对应的分区,不会扫描其他分区的数据。此时该分区内所有行的logging_day值完全相同,在分区内部再用logging_day做排序或主键索引属于完全冗余的操作。
2. 两种写法的查询流程对比
第一种写法(不含logging_day的排序/主键)
建表语句:
CREATE TABLE IF NOT EXISTS new_table ( logging_day Date, sensor_name String, ts DateTime, ts_hour DateTime MATERIALIZED toStartOfHour(ts), ) ENGINE=MergeTree() PARTITION BY logging_day ORDER BY (sensor_name, ts_hour, ts) PRIMARY KEY (sensor_name, ts_hour);
查询流程:
- 第一步:通过
logging_day过滤出目标分区,仅扫描该分区内的数据。 - 第二步:在分区内,利用主键
sensor_name, ts_hour快速定位到sensor_name='foo'且ts_hour='2024-04-20 13:00:00'的连续行范围(因ORDER BY与主键匹配,数据按该顺序连续存储),直接读取对应数据即可。
第二种写法(含logging_day的排序/主键)
建表语句:
CREATE TABLE IF NOT EXISTS new_table ( logging_day Date, sensor_name String, ts DateTime, ts_hour DateTime MATERIALIZED toStartOfHour(ts), ) ENGINE=MergeTree() PARTITION BY logging_day ORDER BY (logging_day, sensor_name, ts_hour, ts) PRIMARY KEY (logging_day, sensor_name, ts_hour);
查询流程:
- 第一步:同样通过
logging_day过滤出目标分区。 - 第二步:在分区内,主键的第一个字段
logging_day值完全一致,实际还是靠sensor_name和ts_hour定位数据。由于主键多了一个冗余字段,索引的存储体积更大,会微小增加索引查找的开销,无法带来任何性能提升。
3. 额外注意事项
- 原建表语句存在语法错误:
ts DateTime行末尾缺少逗号,需要修正后才能正常执行。 - 即便查询条件中没有
logging_day,第一种写法的索引更紧凑,整体性能依然优于第二种写法。
内容的提问来源于stack exchange,提问作者ningl
相关产品推荐
相关产品推荐

