ClickHouse 架构
概述
ClickHouse 是俄罗斯 Yandex 开源的 OLAP 数据库,以极致查询速度著称:列式存储、MergeTree 引擎、向量化执行让它能在毫秒级完成海量聚合。本文讲清列式存储原理、MergeTree 引擎族、分区与排序键、向量化执行。
一、ClickHouse 定位
| 特性 | 说明 |
|---|---|
| 类型 | OLAP(分析型)数据库 |
| 引擎 | MergeTree 家族 |
| 查询 | SQL(类 MySQL) |
| 特点 | 查询极快、压缩率高 |
| 适用 | 大屏、报表、即席分析 |
| 对比 | OLTP | OLAP |
|---|---|---|
| 读写 | 高频小写 | 大吞吐写 + 海量读 |
| 数据 | 行 | 列 |
| 场景 | 交易系统 | 分析系统 |
二、列式存储
2.1 原理
行式存储:
id: 1, name: A, val: 10 | 2, B, 20 | 3, C, 30
按行连续存储
列式存储:
id: 1 2 3
name: A B C
val: 10 20 30
按列连续存储2.2 优势
| 优势 | 说明 |
|---|---|
| 只读所需列 | 跳过无关列 |
| 高压缩 | 同列数据相似,压缩率高 |
| 向量化 | 列数据连续,适合 SIMD |
| 聚合快 | 同列操作高效 |
分析查询只取少数列:
列式存储省 IO,速度快几十倍三、MergeTree 引擎族
3.1 引擎家族
| 引擎 | 用途 |
|---|---|
| MergeTree | 基础(支持分区/排序/主键) |
| ReplacingMergeTree | 去重 |
| SummingMergeTree | 预聚合求和 |
| AggregatingMergeTree | 预聚合(复杂函数) |
| Distributed | 分布式表 |
| Kafka/MySQL/JDBC | 外部集成 |
3.2 MergeTree 核心机制
写入:按分区生成数据 part(不可变)
后台:parts 异步合并(Merge)
查询:合并 parts 数据| 机制 | 说明 |
|---|---|
| 分区 | 数据按分区键隔离 |
| 排序键 | 数据按排序键有序 |
| 主键 | 稀疏索引 |
| 合并 | 后台合并小 part |
写入快:只追加 part
查询快:分区裁剪 + 索引四、分区与排序键
4.1 分区键(PARTITION BY)
sql
CREATE TABLE orders (
order_id UInt64,
amount Float64,
order_date Date
) ENGINE = MergeTree
PARTITION BY toYYYYMM(order_date) -- 按月分区
ORDER BY (order_date, order_id); -- 排序键| 分区作用 | 说明 |
|---|---|
| 数据隔离 | 分区独立 |
| 查询裁剪 | 跳过无关分区 |
| 管理 | 按分区删/合并 |
分区建议:
粒度适中(天/月)
过细 → part 多、合并压力4.2 排序键(ORDER BY)
| 作用 | 说明 |
|---|---|
| 数据有序 | 按排序键存储 |
| 稀疏索引 | 主键索引定位 |
| 聚合优化 | 排序相关聚合 |
ORDER BY 是主键的基础:
查条件包含排序键前缀 → 索引生效五、主键索引(稀疏索引)
5.1 原理
每 N 行生成一个索引条目(稀疏):
不存每行,只存"块"的边界值
查询用二分定位数据块| 对比 | 稠密索引 | 稀疏索引 |
|---|---|---|
| 存储 | 每行 | 每块 |
| 定位 | 精确 | 块级 |
| 内存 | 大 | 小 |
索引粒度 = index_granularity(默认 8192 行)5.2 生效条件
| 条件 | 说明 |
|---|---|
| 前缀匹配 | 查询含排序键前缀 |
| 范围/等值 | 条件可裁剪 |
六、向量化执行
6.1 原理
数据按列批量处理(每批 1024 行):
一次指令处理一批数据(SIMD)
避免逐行解释| 对比 | 逐行 | 向量化 |
|---|---|---|
| 方式 | 循环逐行 | 批量 SIMD |
| 速度 | 慢 | 快数十倍 |
| CPU 利用 | 低 | 高 |
6.2 并行
查询多核并行:
分区/part 级并行
GROUP BY 分布式聚合| 并行维度 | 说明 |
|---|---|
| 分区并行 | 各分区并行扫 |
| 多线程 | 单查询多线程 |
| 集群 | 分片并行 |
七、查询流程
1. 解析 SQL → 查询计划
2. 分区裁剪(跳过分区)
3. 索引裁剪(定位块)
4. 列读取(只取所需列)
5. 向量化执行
6. 结果合并返回| 优化点 | 说明 |
|---|---|
| 裁剪 | 少读数据 |
| 列式 | 少读字段 |
| 向量化 | 快计算 |
八、适用与限制
8.1 适用
| 场景 | 说明 |
|---|---|
| 实时大屏 | 秒级聚合 |
| 明细查询 | 海量明细 |
| 日志分析 | 时间序列 |
| 报表 | 多维度 |
8.2 限制
| 限制 | 说明 |
|---|---|
| 不支持事务 | 分析场景可接受 |
| 单行更新弱 | 靠重写/合并 |
| JOIN 弱 | 大表 Join 需优化 |
| 高并发点查弱 | OLAP 定位 |
常见问题速查
| 问题 | 原因与处理 |
|---|---|
| 查询慢 | 检查分区/索引裁剪 |
| 数据重复 | 用 Replacing 系列引擎 |
| part 太多 | 调整分区粒度/合并 |
| 内存不足 | 限制内存、优化查询 |
| 压缩率低 | 检查列类型与压缩算法 |