ClickHouse 教程 / ClickHouse 简介与架构
ClickHouse 简介与架构
1. ClickHouse 的历史
ClickHouse 最初由俄罗斯搜索引擎巨头 Yandex 开发,用于其网站流量分析工具 Yandex Metrica——全球第三大网站分析平台。
| 时间节点 | 里程碑 |
|---|---|
| 2009 年 | Yandex Metrica 内部项目启动,名为 ClickHouse(Clickstream Data Warehouse) |
| 2014 年 | 在 Yandex 内部全面替代原有 OLAP 系统 |
| 2016 年 | 正式开源,发布到 GitHub |
| 2021 年 | 成立 ClickHouse, Inc.,获得 B 轮融资 |
| 2022 年 | 推出 ClickHouse Cloud(全托管云服务) |
| 2024 年 | 社区持续壮大,成为最流行的开源 OLAP 数据库之一 |
💡 提示:ClickHouse 这个名字来源于 ClickHouse = Click(点击流)+ House(数据仓库),同时也致敬了 ClickHouse 所基于的 LSM-Tree 存储引擎思想。
2. 列式存储 vs 行式存储
ClickHouse 采用列式存储,这是其高性能的核心基础。
2.1 存储模型对比
| 特性 | 行式存储(如 MySQL) | 列式存储(如 ClickHouse) |
|---|---|---|
| 数据组织方式 | 按行连续存储 | 按列连续存储 |
| 适合场景 | OLTP(增删改查) | OLAP(分析查询) |
| 读取效率 | 读整行快 | 读少量列极快 |
| 压缩效率 | 低(数据类型混杂) | 高(同列数据类型相同) |
| 写入效率 | 单行写入快 | 批量写入效率高 |
| 索引方式 | B+Tree | 稀疏索引 + 跳数索引 |
2.2 直观示例
假设有一张用户表:
行式存储(Row-oriented):
Row 1: [1, 'Alice', 28, 'Beijing']
Row 2: [2, 'Bob', 35, 'Shanghai']
Row 3: [3, 'Carol', 22, 'Guangzhou']
列式存储(Column-oriented):
Column 'id': [1, 2, 3]
Column 'name': ['Alice', 'Bob', 'Carol']
Column 'age': [28, 35, 22]
Column 'city': ['Beijing', 'Shanghai', 'Guangzhou']
当执行 SELECT avg(age) FROM users 时:
- 行式存储:需要读取所有列的数据,仅使用
age列 - 列式存储:仅读取
age列,I/O 减少约 75%
⚠️ 注意:列式存储不适合高频单行更新(UPDATE ... WHERE id = ?),这类操作在 ClickHouse 中代价很高。
3. ClickHouse 核心特性
3.1 向量化执行引擎
ClickHouse 不按行逐条处理,而是按列块(Block) 向量化处理:
-- ClickHouse 内部将数据划分为 granule(默认 8192 行)
-- 对整个 granule 进行 SIMD 向量化计算
SELECT sum(price * quantity) AS total
FROM orders
WHERE order_date >= '2026-01-01';
3.2 列式压缩
由于同列数据类型相同且数据相似度高,压缩率极高:
| 数据类型 | 典型压缩率 | 常用 Codec |
|---|---|---|
| 时间序列数值 | 10:1 ~ 20:1 | Delta + ZSTD |
| 日志文本 | 5:1 ~ 10:1 | LZ4 |
| 枚举/低基数 | 20:1 ~ 100:1 | LZ4 + LowCardinality |
| 稀疏整数 | 50:1 ~ 200:1 | Gorilla |
3.3 完整的 SQL 支持
ClickHouse 支持标准 SQL 语法,并扩展了大量分析函数:
-- 标准 SQL
SELECT
toStartOfMonth(order_date) AS month,
count() AS orders,
sum(amount) AS revenue,
uniq(user_id) AS unique_users,
quantile(0.95)(amount) AS p95_amount
FROM orders
GROUP BY month
ORDER BY month;
3.4 其他关键特性
- 实时数据摄入:支持秒级数据写入
- 多副本复制:内置 ReplicatedMergeTree 引擎
- 分片集群:支持水平分片 + 分布式查询
- 物化视图:实时预聚合
- 近似计算:HyperLogLog、Count-Min Sketch 等
- 多表 JOIN:支持 ALL/ANY/ASOF JOIN
4. 架构组件
4.1 整体架构
┌──────────────────────────────────────────────────┐
│ 客户端层 │
│ clickhouse-client / JDBC / HTTP / gRPC │
└──────────────────┬───────────────────────────────┘
│
┌──────────────────▼───────────────────────────────┐
│ ClickHouse Server │
│ ┌──────────┐ ┌───────────┐ ┌──────────────────┐ │
│ │ TCP 服务 │ │ HTTP 服务 │ │ Inter-Server │ │
│ │ (9000) │ │ (8123) │ │ 通信 (9009) │ │
│ └──────────┘ └───────────┘ └──────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 查询执行引擎(向量化) │ │
│ └──────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 表引擎(MergeTree 系列) │ │
│ └──────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 存储层(列式文件 + 索引) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
│ │
▼ ▼
┌───────────┐ ┌──────────────┐
│ 本地磁盘 │ │ ZooKeeper / │
│ (data/) │ │ Keeper │
└───────────┘ └──────────────┘
4.2 核心组件说明
| 组件 | 作用 | 说明 |
|---|---|---|
| MergeTree 引擎 | 数据存储与合并 | 后台异步合并数据部分(parts) |
| ReplicatedMergeTree | 数据复制 | 通过 ZooKeeper/Keeper 实现多副本同步 |
| ZooKeeper / Keeper | 分布式协调 | 管理副本状态、DDL 任务、选主 |
| Distributed 表 | 分布式查询 | 路由查询到各分片,合并结果 |
| 物化视图 | 实时预计算 | 写入时触发,维护聚合结果 |
| 字典(Dictionary) | 外部数据映射 | 关联外部数据源的键值映射 |
4.3 数据写入与合并流程
INSERT INTO table VALUES (...)
│
▼
写入内存 buffer
│
▼ (后台线程,约每 10s)
flush 到磁盘,形成 data part
│
▼ (后台线程,异步调度)
merge: 将多个小 part 合并为大 part
(此过程中数据去重、TTL 过期清理等)
⚠️ 注意:ClickHouse 的 UPDATE 和 DELETE 是异步操作(通过 mutation),不会立即生效,实际执行时会重写数据部分。
5. 适用场景 vs 不适用场景
5.1 适用场景 ✅
| 场景 | 说明 | 典型案例 |
|---|---|---|
| OLAP 分析 | 大规模聚合查询 | BI 报表、即席查询 |
| 日志分析 | 海量日志存储与检索 | ELK 替代方案 |
| 实时数据分析 | 秒级延迟的数据看板 | 实时大屏 |
| 时序数据 | 监控指标存储与查询 | Prometheus 长期存储 |
| 用户行为分析 | 点击流、埋点数据 | 漏斗、留存分析 |
| 广告归因 | 广告效果分析 | ROI 计算 |
5.2 不适用场景 ❌
| 场景 | 原因 | 替代方案 |
|---|---|---|
| OLTP 事务 | 不支持 ACID 事务 | MySQL / PostgreSQL |
| 高频单行更新 | UPDATE/DELETE 代价高 | MySQL / PostgreSQL |
| 低延迟点查 | 稀疏索引不适合 | Redis / HBase |
| 频繁 DELETE | 删除是异步 mutation | MySQL / PostgreSQL |
| 键值缓存 | 不适合做缓存层 | Redis / Memcached |
-- ❌ 不推荐:逐行更新
UPDATE users SET status = 'active' WHERE user_id = 12345;
-- ✅ 推荐:批量插入
INSERT INTO events (user_id, event_type, event_time, data)
VALUES
(1, 'click', now(), '{"page": "home"}'),
(2, 'purchase', now(), '{"amount": 99.5}'),
(3, 'click', now(), '{"page": "search"}');
6. 与主流数据库对比
| 特性 | ClickHouse | MySQL | PostgreSQL | Apache Druid | Apache Doris |
|---|---|---|---|---|---|
| 存储模型 | 列式 | 行式 | 行式 | 列式 | 列式 |
| 擅长场景 | OLAP | OLTP | OLTP/混合 | OLAP(实时) | OLAP |
| SQL 兼容性 | 高 | 标准 | 最高 | 较低(原生SQL) | 高 |
| 事务支持 | ❌ | ✅ | ✅ | ❌ | ❌ |
| 实时写入 | 高吞吐 | 中 | 中 | 高吞吐 | 高吞吐 |
| JOIN 性能 | 一般 | 好 | 好 | 一般 | 好 |
| 运维复杂度 | 中 | 低 | 低 | 高 | 中 |
| 社区生态 | 活跃 | 最大 | 大 | 中 | 活跃 |
6.1 选型建议
-- 场景:需要实时分析 10 亿级日志数据
-- 选择:ClickHouse
-- 场景:需要事务 + 分析混合
-- 选择:PostgreSQL (OLTP) + ClickHouse (OLAP)
-- 通过 ETL 或 CDC 同步数据
-- 场景:实时指标看板,亚秒级响应
-- 选择:ClickHouse + 物化视图 预聚合
7. ClickHouse Cloud 简介
ClickHouse Cloud 是官方提供的全托管服务,适合不想自建集群的团队:
| 特性 | 说明 |
|---|---|
| Serverless 架构 | 按需扩缩容,按实际使用计费 |
| 自动运维 | 无需管理 ZooKeeper、磁盘、副本 |
| 内置 S3 存储 | 数据持久化到对象存储 |
| 多区域部署 | 支持 AWS / GCP / Azure |
| SQL Console | 内置 Web IDE |
| 数据导入 | 支持 Kafka / S3 / DataLens 集成 |
💡 提示:对于中小团队,ClickHouse Cloud 可以大幅降低运维成本,开发阶段可用免费试用额度。
8. 第一个 ClickHouse 查询
安装 ClickHouse 后(详见下一章),尝试以下查询:
-- 创建本地表
CREATE TABLE IF NOT EXISTS hits_local
(
WatchID UInt64,
UserID UInt64,
EventTime DateTime,
URL String,
Title String,
Referer String
)
ENGINE = MergeTree()
ORDER BY (EventTime, UserID);
-- 插入测试数据
INSERT INTO hits_local VALUES
(1001, 1, '2026-05-10 08:00:00', '/home', 'Homepage', ''),
(1002, 2, '2026-05-10 08:05:00', '/product/1', 'Product A', '/home'),
(1003, 1, '2026-05-10 08:10:00', '/cart', 'Cart', '/product/1'),
(1004, 3, '2026-05-10 09:00:00', '/home', 'Homepage', ''),
(1005, 2, '2026-05-10 09:15:00', '/checkout', 'Checkout', '/cart');
-- 查询:每小时 UV 和 PV
SELECT
toStartOfHour(EventTime) AS hour,
count() AS page_views,
uniq(UserID) AS unique_visitors
FROM hits_local
GROUP BY hour
ORDER BY hour;
预期输出:
┌────────────────hour─┬─page_views─┬─unique_visitors─┐
│ 2026-05-10 08:00:00 │ 3 │ 2 │
│ 2026-05-10 09:00:00 │ 2 │ 2 │
└─────────────────────┴────────────┴─────────────────┘