ClickHouse 主键索引详解:不是唯一标识,而是排序规则

文章目录

在 MySQL 中,主键是每一行的"身份证",必须唯一且不能为空。但到了 ClickHouse,你发现主键可以重复,甚至可以是 NULL------这完全颠覆了传统认知。那么,ClickHouse 的主键到底是什么?它的作用是什么?本文将彻底讲清 ClickHouse 主键索引的本质、用法和最佳实践。


一、开篇:一个颠覆认知的概念

如果你从 MySQL 转过来,第一次看到 ClickHouse 的主键定义,大概率会困惑:

sql 复制代码
-- MySQL 的主键:必须唯一,不能为空
CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

-- ClickHouse 的主键:可以重复,可以为 NULL
CREATE TABLE user_log (
    event_time DateTime,
    user_id UInt64,
    event_type String
) ENGINE = MergeTree()
PRIMARY KEY (event_time, user_id);  -- 允许重复值

核心区别

对比项 MySQL 主键 ClickHouse 主键
唯一性 ✅ 强制唯一 ❌ 允许重复
作用 唯一标识一行 决定磁盘排序顺序 + 稀疏索引依据
能否为 NULL ❌ 不能 ✅ 能
索引类型 密集索引(B-Tree) 稀疏索引
与 ORDER BY 关系 独立 紧密关联(可等价)

二、ClickHouse 主键的本质:排序键 + 稀疏索引

在 ClickHouse 的 MergeTree 引擎中,PRIMARY KEYORDER BY 关系非常紧密,甚至经常被混用。

2.1 主键 = 排序键

数据在磁盘上按主键列的顺序物理存储。这是 ClickHouse 主键最核心的作用。

sql 复制代码
-- 数据会先按 event_time 排序,再按 user_id 排序
PRIMARY KEY (event_time, user_id)

效果

  • event_time 相近的行挨在一起
  • 同一时间点的 user_id 也有序

2.2 主键 = 稀疏索引的依据

ClickHouse 的主键索引是稀疏索引:每隔 8192 行,记录这一行的主键值到索引文件。

复制代码
┌─────────────────────────────────────────────────────────────┐
│  Granule 0(行1~8192):event_time = 2025-01-01 10:00:00    │
│  Granule 1(行8193~16384):event_time = 2025-01-01 10:01:00│
│  Granule 2(行16385~24576):event_time = 2025-01-01 10:02:00│
└─────────────────────────────────────────────────────────────┘

注意:稀疏索引不记录每一行,只记录每个 granule 的起始行的主键值。

2.3 主键与 ORDER BY 的关系

写法 效果 说明
只写 ORDER BY ORDER BY 即主键 ClickHouse 会将其同时作为主键
同时写 ORDER BYPRIMARY KEY PRIMARY KEY 必须是 ORDER BY 的前缀 元数据可冗余,但排序仍按 ORDER BY
只写 PRIMARY KEY ORDER BY 默认等于 PRIMARY KEY 主键即排序键
sql 复制代码
-- 以下三种写法等价
CREATE TABLE t1 (id UInt64, name String) ENGINE = MergeTree() ORDER BY id;
CREATE TABLE t2 (id UInt64, name String) ENGINE = MergeTree() PRIMARY KEY id;
CREATE TABLE t3 (id UInt64, name String) ENGINE = MergeTree() ORDER BY id PRIMARY KEY id;

三、主键的作用

作用 说明 示例
数据排序 相同主键前缀的行物理上相邻 提高压缩率,加速范围查询
稀疏索引 快速跳过不相关的 granule WHERE event_time > '2025-01-01' 快速定位起始 granule
数据去重 配合 ReplacingMergeTree 使用(需 ORDER BY 去重键必须是 ORDER BY 的前缀
分区内排序 每个分区内独立排序 减少跨分区数据混叠

注意 :ClickHouse 主键不保证唯一性 ,也不会阻止插入重复值。如果需要去重,需使用 ReplacingMergeTree 表引擎。


四、主键索引的工作方式

4.1 查询执行流程

WHERE event_time = '2025-01-01 10:01:30' 为例:

步骤 操作 说明
1 二分查找索引 在索引中找到 event_time >= 目标值 的第一个 granule --- Granule 1
2 定位到 granule 根据索引条目,定位到 Granule 1 在磁盘上的位置
3 块内扫描 在 Granule 1(8192行)内逐行扫描,找到匹配的行

结论:主键索引帮你快速跳过不相关的 granule,但 granule 内仍需线性扫描。

4.2 为什么不能精确定位到行?

因为 ClickHouse 是列式存储 + 压缩。如果索引精确定位到行,需要解压并读取该行所有列,反而更慢。块内扫描可以批量解压、向量化执行,效率更高。


五、主键设计的最佳实践

5.1 把高频过滤列放在最前面

sql 复制代码
-- ✅ 好的设计:查询总是带 event_date
PRIMARY KEY (event_date, user_id)

-- ❌ 差的设计:user_id 在第一位,但查询很少用它过滤
PRIMARY KEY (user_id, event_date)

5.2 主键不宜过多列

sql 复制代码
-- ❌ 过多列
PRIMARY KEY (col1, col2, col3, col4, col5)

-- ✅ 1-3 列最佳
PRIMARY KEY (event_date, user_id)

原因:主键列越多,排序开销越大,索引粒度越粗,块内扫描范围越大。

5.3 高基数 vs 低基数列的顺序

查询模式 推荐顺序 原因
等值查询为主(WHERE user_id = 123 高基数在前 快速缩小范围
范围查询为主(WHERE event_date > '2025-01-01' 低基数在前 范围字段放前面,利用有序性

5.4 主键与分区键的配合

sql 复制代码
-- 按天分区,按时间+用户排序
PARTITION BY toYYYYMMDD(event_date)
PRIMARY KEY (event_date, user_id)

效果

  • 分区裁剪 → 只扫描相关分区
  • 主键索引 → 快速定位分区内的 granule

六、常见误区与澄清

误区 真相
"主键必须唯一" ❌ ClickHouse 主键允许重复,甚至全是 NULL
"主键是行级标识" ❌ ClickHouse 没有行级唯一标识的概念
"有了主键,点查就快" ⚠️ 主键帮助跳过 granule,但 granule 内仍需扫描 8192 行
"主键越多越好" ❌ 主键列过多会导致排序开销大,索引粒度变粗
"PRIMARY KEY 和 ORDER BY 可以完全不同" ⚠️ 可以不同,但 PRIMARY KEY 必须是 ORDER BY 的前缀

七、与稀疏索引的关系

概念 关系 说明
主键 决定了按什么列排序 是数据物理顺序的定义
稀疏索引 基于主键建立的索引 每隔 8192 行记录主键值到索引文件
主键索引 两者合称 主键 + 稀疏索引共同构成

一句话:主键定义规则,稀疏索引基于规则建立路标。


八、总结

问题 答案
ClickHouse 主键是什么? 排序键 + 稀疏索引依据,决定数据的物理顺序
与 MySQL 主键的区别? 不保证唯一、可为 NULL、不是行级标识
主键的作用? 排序、压缩、稀疏索引、数据去重(配合特定引擎)
主键能加速点查吗? 有限,只能跳过 granule,块内仍需扫描
主键设计原则? 高频过滤列在前,1-3 列最佳
PRIMARY KEY 和 ORDER BY 的关系? 紧密相关,PRIMARY KEY 必须是 ORDER BY 的前缀

一句话记住

ClickHouse 的主键不是"唯一身份证",而是"排序规则 + 稀疏索引依据"。它决定了数据在磁盘上的物理顺序,帮助快速跳过不相关的数据块,但不保证唯一性,也不精确定位到行。


如需深入了解 ClickHouse 的稀疏索引原理、ORDER BY 设计、分区策略等内容,请持续关注本专栏《ClickHouse 一站式从入门到实战》系列文章。

相关推荐
海南java第二人21 小时前
ClickHouse 列式存储深度解析:优点、缺点与选型实战
数据库·clickhouse
努力攻坚操作系统2 天前
ClickHouse虚拟列
clickhouse
海南java第二人2 天前
ClickHouse 备份与恢复完全指南:从物理拷贝到内置备份的实战选择
clickhouse·备份与恢复
海南java第二人2 天前
ClickHouse Sharding 分片与 Partitioning 分区:区别、联系与生产实践
clickhouse·分区·分片
狼与自由4 天前
mysql到clickhouse
数据库·mysql·clickhouse
云天AI实战派4 天前
跨境出海全流程实战:用 Medusa + Hyperswitch + ClickHouse 搭建落地页、支付订阅、客服工单与多语言 SEO 闭环
大数据·人工智能·clickhouse·独立开发·跨境出海·medusa
海南java第二人5 天前
ClickHouse 实际应用类面试通关:项目案例、生产踩坑与实战经验
clickhouse·面试·实际应用类
meijinmeng6 天前
ClickHouse Kubernetes集群部署与维护文档
clickhouse
努力攻坚操作系统6 天前
ClickHouse详细教程
大数据·数据库·clickhouse