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 一站式从入门到实战》系列文章。

相关推荐
SelectDB技术团队2 天前
从 ClickHouse 迁移到 Doris:SQL 兼容、同步与验证清单
数据库·人工智能·sql·clickhouse·apache doris·selectdb·湖仓架构升级
java_logo4 天前
Docker 部署 ClickHouse Server:轻松搭建列式 OLAP 分析数据库平台
数据库·clickhouse·docker·私有化部署·olap·列式数据库·轩辕镜像
西风未眠8 天前
ClickHouse数据库引擎引起的数据丢失
数据库·clickhouse·故障
李兆龙的博客10 天前
从一到无穷大 #86:ClickHouse 收购 RunReveal——可观测性竞争进入宏观调控阶段
clickhouse
Gain_chance14 天前
大数据毕业设计实战|Data Insight Platform:用 FastAPI + ClickHouse 打造低门槛全链路数据洞察平台
大数据·数据库·clickhouse·毕业设计·fastapi
迷迭香yy17 天前
A股数据本地存储方案对比JSON、SQLite、ClickHouse、DuckDB怎么选 IG50免费开源股票数据API接口
clickhouse·sqlite·json
leisoo809718 天前
本地股票数据深度对比ClickHouse与Parquet存储方案 IG50免费开源股票数据API接口
clickhouse·开源
A.说学逗唱的Coke20 天前
【数据库专题】ClickHouse 深度实战:从列式存储原理到 PB 级海量日志与可观测性分析
数据库·clickhouse·硬件架构
吉甫作诵1 个月前
阿里云 ECS 安装 ClickHouse:密码配置、目录迁移与基础调优
大数据·clickhouse
NineData1 个月前
NineData Oracle 到 ClickHouse 数据迁移完整流程:结构复制、全量装载、增量同步
数据库·clickhouse·oracle·数据迁移·ninedata·增量同步·数据迁移工具