1. ClickHouse 入门:从零开始认识这个列式存储怪兽
1.1 发展史:一个俄罗斯创业公司的意外走红
ClickHouse 的故事,得从 2009 年说起。
当时俄罗斯最大的搜索引擎 Yandex 面临一个头疼的问题——网页流量分析系统 Metrica 需要实时处理海量数据。传统的数据库根本扛不住。于是他们内部启动了一个叫 "ClickHouse" 的项目,目标很明确:在 1 秒内完成对数十亿行数据的聚合查询。
2016 年,Yandex 把这个项目开源了。说实话,刚开源那会儿关注度并不高。我记得 2017 年我第一次接触它时,社区文档还只有俄文版,啃起来相当痛苦。但真正用过一次后,我就被它的查询速度震撼到了——同样的 SQL,在 MySQL 上跑 30 秒的聚合查询,ClickHouse 只要 200 毫秒。
后来它迅速在国内外走红。现在 GitHub 上已经有 38k+ star,国内大厂几乎都在用。我个人觉得,它的成功不是因为什么花哨的概念,而是解决了真实世界中「快」这个核心痛点。
1.2 核心特性:为什么它能这么快?
ClickHouse 不是万能的,但在 OLAP 场景下,它确实快得离谱。原因在于几个关键设计:
- 列式存储:只读取需要的列,IO 量大幅减少。你想想看,一张 200 列的表,如果只查 3 列,行式存储要读全部 200 列,列式存储只读 3 列,差距是数量级的。
- 向量化执行:利用 CPU 的 SIMD 指令集,一次处理一批数据,而不是一行一行处理。我在项目中实测过,向量化比逐行处理快 5-10 倍。
- 数据压缩:列式存储天然适合压缩,同一列的数据类型相同,压缩比通常能达到 5:1 甚至 10:1。我曾经把一个 500GB 的 MySQL 表导入 ClickHouse,磁盘占用只有 80GB。
- 分布式架构:数据自动分片到多台机器,查询可以并行执行。理论上可以线性扩展。
一句话总结:ClickHouse 是为「读多写少、大宽表、高吞吐、低延迟」的 OLAP 场景量身定做的。
1.3 OLAP 与 OLTP:两个世界的对决
很多新手会问:ClickHouse 能不能替代 MySQL?我的回答是:别这么干,会出事的。
它们根本不是一个物种。来看个对比:
| 维度 | OLTP(如 MySQL) | OLAP(如 ClickHouse) |
|---|---|---|
| 典型场景 | 订单系统、用户登录 | 报表分析、用户行为分析 |
| 读写模式 | 频繁小批量写入 | 批量导入,大量读取 |
| 查询特征 | 点查(查一条记录) | 聚合扫描(查几亿行) |
| 数据更新 | 支持频繁 UPDATE/DELETE | 不支持或代价极高 |
| 存储格式 | 行式存储 | 列式存储 |
| 并发能力 | 高并发(数千 QPS) | 低并发(数十 QPS) |
避坑指南:我曾经见过一个团队把 ClickHouse 当 MySQL 用,频繁执行单行 INSERT 和 UPDATE,结果性能惨不忍睹。记住:ClickHouse 不适合做事务型系统,它只适合做分析型系统。
1.4 适用场景:什么时候该用它?
根据我的实战经验,以下场景 ClickHouse 表现极佳:
- 用户行为分析:埋点数据、页面 PV/UV、漏斗分析
- 监控与日志:服务器指标、应用日志、网络流量
- BI 报表:销售日报、财务汇总、多维交叉分析
- 时序数据:IoT 设备数据、金融行情数据
不适合的场景:
- 需要频繁行级更新的业务系统
- 需要事务 ACID 保证的场景
- 单条数据查询(点查)
1.5 环境搭建:单机模式
先来个最简单的单机部署。我习惯用 Docker,省心。
# 拉取镜像
docker pull clickhouse/clickhouse-server:latest
# 启动容器
docker run -d --name clickhouse-server \
-p 8123:8123 \
-p 9000:9000 \
clickhouse/clickhouse-server:latest
# 进入容器
docker exec -it clickhouse-server clickhouse-client
启动后,用客户端连上去试试:
clickhouse-client --host 127.0.0.1 --port 9000
# 查看版本
SELECT version();
# 建个表试试
CREATE TABLE test.event (
event_date Date,
event_type String,
user_id UInt64,
amount Float64
) ENGINE = MergeTree()
ORDER BY (event_date, event_type);
# 插入几条数据
INSERT INTO test.event VALUES
('2024-01-01', 'click', 1001, 12.5),
('2024-01-01', 'view', 1002, 0),
('2024-01-02', 'click', 1003, 8.0);
# 查询
SELECT event_date, count() AS cnt, sum(amount) AS total
FROM test.event
GROUP BY event_date;
小提示:第一次建表时,ORDER BY 子句特别重要。它决定了数据的物理排序,直接影响查询性能。我一般把最常用的过滤字段放在前面。
1.6 环境搭建:集群模式
单机玩熟了,就该上集群了。集群模式需要配置 ZooKeeper 来协调节点。
我直接给一个三节点集群的 docker-compose 配置:
version: '3'
services:
zookeeper:
image: zookeeper:3.7
ports:
- "2181:2181"
clickhouse-1:
image: clickhouse/clickhouse-server:latest
ports:
- "8123:8123"
- "9000:9000"
volumes:
- ./configs/node1/config.xml:/etc/clickhouse-server/config.xml
- ./configs/node1/metrika.xml:/etc/clickhouse-server/metrika.xml
clickhouse-2:
image: clickhouse/clickhouse-server:latest
ports:
- "8124:8123"
- "9001:9000"
volumes:
- ./configs/node2/config.xml:/etc/clickhouse-server/config.xml
- ./configs/node2/metrika.xml:/etc/clickhouse-server/metrika.xml
clickhouse-3:
image: clickhouse/clickhouse-server:latest
ports:
- "8125:8123"
- "9002:9000"
volumes:
- ./configs/node3/config.xml:/etc/clickhouse-server/config.xml
- ./configs/node3/metrika.xml:/etc/clickhouse-server/metrika.xml
关键配置在 metrika.xml 里,需要定义分片和副本:
<yandex>
<clickhouse_remote_servers>
<my_cluster>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>clickhouse-1</host>
<port>9000</port>
</replica>
<replica>
<host>clickhouse-2</host>
<port>9000</port>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>clickhouse-3</host>
<port>9000</port>
</replica>
</shard>
</my_cluster>
</clickhouse_remote_servers>
</yandex>
注意:生产环境中,ZooKeeper 一定要单独部署,不要和 ClickHouse 混布。我曾经因为图省事把 ZK 和 ClickHouse 放一起,结果 ZK 磁盘写满导致整个集群挂了。血的教训。
1.7 知识体系总览
为了让你对本章内容有个整体印象,我画了张图:
这张图把本章的核心知识点串起来了。你可以看到,ClickHouse 的底层设计(列式存储、向量化执行)决定了它的性能上限,而 OLAP vs OLTP 的对比则帮你判断「什么时候该用它」。环境搭建是动手的第一步,后面的章节我们会一步步深入。