这是《ClickHouse 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 ClickHouse 是面向实时分析的列式数据库。它不适合做交易系统,但在海量事件扫描、多维聚合、漏斗留存、监控指标和实时看板上表现出色。
一句话概括:MySQL 帮你精准找到几行数据,ClickHouse 帮你快速算完几亿行数据。
1.1 为什么行存不适合 OLAP
假设事件表有 100 列,而分析只用到 4 列:
SELECT city_id, count(*), sum(amount)
FROM events
WHERE event_date = '2026-08-25'
GROUP BY city_id;
行存数据库必须读取完整行,即使只需要几个字段:
Row: [id, user_id, device, ip, ua, event_id, ... 100 columns]
列存数据库按列保存:
event_date: 2026-08-25, 2026-08-25, ...
city_id: 1, 2, 1, ...
amount: 99, 199, 39, ...
ClickHouse 只读取需要的列,因此:
- IO 更少;
- 压缩率更高;
- CPU 可以按列批量计算;
- 聚合性能显著提升。
1.2 OLTP 与 OLAP 的区别
| 维度 | OLTP | OLAP |
|---|---|---|
| 系统示例 | MySQL | ClickHouse |
| 查询类型 | 点查、短事务 | 扫描、聚合、Join |
| 单次行数 | 少量 | 大量 |
| 写入模式 | 随机增删改 | 批量追加 |
| 延迟要求 | 毫秒级事务 | 毫秒到秒级分析 |
| 更新 | 常态 | 谨慎或异步 |
| 索引 | B+Tree | 稀疏索引、跳数索引 |
| 存储 | 行存 | 列存 |
典型分工:
MySQL:下单、支付、库存、账户
Kafka:事件流
ClickHouse:行为分析、看板、漏斗、留存
Elasticsearch:搜索和日志检索
1.3 ClickHouse 的核心特性
1.3.1 列式存储
数据按列文件保存,适合只读部分列的分析。
1.3.2 向量化执行
CPU 一次处理一批值,而不是一行一行解释执行。
行执行:sum(row1.amount), sum(row2.amount), ...
向量化:sum([amount1, amount2, amount3, ...])
1.3.3 数据压缩
同一列的数据类型和模式相近,压缩效果好。
| 数据 | 压缩效果 |
|---|---|
| 枚举、状态 | 极好 |
| 时间戳 | 好 |
| 用户 ID | 中等 |
| 随机字符串 | 一般 |
1.3.4 MergeTree 引擎
ClickHouse 最重要的表引擎家族,支持:
- 分区;
- 主键排序;
- 稀疏索引;
- 后台合并;
- 副本;
- TTL;
- 投影;
- 多种合并语义。
1.4 表引擎概览
| 引擎 | 特点 | 适合 |
|---|---|---|
| MergeTree | 基础分析表 | 通用事实表 |
| ReplacingMergeTree | 合并时保留版本 | 幂等导入 |
| SummingMergeTree | 数值列汇总 | 预聚合 |
| CollapsingMergeTree | sign 折叠 | 变更标记 |
| VersionedCollapsingMergeTree | 按版本折叠 | 乱序变更 |
| ReplicatedMergeTree | 副本 | 生产高可用 |
| Distributed | 分布式查询入口 | 分片集群 |
| Kafka | 消费 Kafka | 实时链路 |
| Memory | 内存表 | 临时数据 |
| Nullable 特性 | 空值处理 | 谨慎使用 |
ClickHouse 的表引擎不是简单配置,而是决定数据合并语义和查询行为的核心设计。
1.5 一个最小事件表
CREATE TABLE events_local
(
event_date Date,
event_time DateTime,
event_id String,
user_id UInt64,
event_type LowCardinality(String),
city_id UInt32,
amount Decimal64(2),
platform LowCardinality(String),
props Map(String, String)
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, event_type, city_id, user_id)
SETTINGS index_granularity = 8192;
设计要点:
PARTITION BY控制数据生命周期和剪枝范围;ORDER BY决定稀疏索引和数据局部性;LowCardinality(String)优化低基数字段;Map适合扩展属性,但不要把高频过滤字段只放 Map;- 金额使用
Decimal64(2)。
查询:
SELECT city_id, count() AS pv, sum(amount) AS gmv
FROM events_local
WHERE event_date = '2026-08-25'
GROUP BY city_id
ORDER BY gmv DESC
LIMIT 20;
1.6 写入模型
ClickHouse 喜欢大批量写入,不喜欢高频小插入。
推荐:
每批 1 万到 10 万行
间隔数百毫秒到数秒
写入端按表和分区合并
失败可重试且幂等
不推荐:
每秒每表几十上百次小 insert
大量小写入会导致 part 过多、后台合并压力增加、查询变慢,甚至触发 “too many parts” 错误。
1.7 与 Elasticsearch 的差异
| 维度 | ClickHouse | Elasticsearch |
|---|---|---|
| 强项 | 大范围聚合分析 | 全文搜索、倒排索引 |
| 文本搜索 | 基础能力 | 强 |
| 多维聚合 | 强 | 可用但成本高 |
| 写入 | 批量 append | 文档实时写入 |
| 更新 | Merge 语义 | 文档 update |
| 日志场景 | 大规模分析 | 检索和排障 |
| 典型用途 | 数仓分析 | 搜索、可观测性 |
常见架构是两者共存:
Kafka
-> Elasticsearch:最近日志检索
-> ClickHouse:长期分析和报表
1.8 适用与不适用场景
适合:
- 用户行为分析;
- 埋点看板;
- 漏斗和留存;
- 监控指标;
- 广告归因;
- 风控离线特征;
- 实时报表;
- 大规模日志聚合。
不适合:
- 在线交易;
- 高频单点更新;
- 强事务;
- 复杂多表实时 JOIN;
- 主数据管理;
- 低延迟键值查询。
本章小结
ClickHouse 通过列存、压缩、向量化执行和 MergeTree 合并模型,为海量分析场景提供高吞吐查询能力。它的设计围绕批量写入、分区剪枝、排序键和预聚合展开。要用好 ClickHouse,关键是明确它是 OLAP 系统,而不是万能数据库。
思考题
- 行存和列存分别适合什么查询?
PARTITION BY和ORDER BY分别解决什么问题?- 为什么 ClickHouse 不适合高频小批量写入?
- ClickHouse 和 Elasticsearch 在日志架构中如何分工?
- 如果要把 MySQL 订单数据同步到 ClickHouse 做报表,你会考虑哪些一致性问题?