3. ClickHouse 快速入门:架构原理、单机部署、建表与基本查询
好,咱们进入 ClickHouse 的世界。这一章我会带你快速上手,从架构原理到实际建表查询,一步到位。我个人习惯是「先理解设计,再动手操作」,这样遇到问题才不会慌。
3.1 ClickHouse 的架构原理
ClickHouse 为什么这么快?说白了,它的设计哲学就是「把每一分硬件性能都榨干」。我刚开始接触时也觉得夸张,直到有一次在项目中用 MySQL 跑百亿级聚合查询,等了 20 分钟没出结果……换成 ClickHouse 后,同样的数据量,3 秒出数。嗯,从那以后我就再也不敢小看列式存储了。
3.1.1 列式存储 vs 行式存储
传统数据库(比如 MySQL)是行式存储,一行数据物理上挨着放。你想想看,如果我只想查某个字段的平均值,它却要把整行数据都读出来,IO 浪费很大。
ClickHouse 采用列式存储,每一列单独存放。查询时只读取需要的列,IO 量骤减。再加上列式数据天然适合压缩,压缩比通常能达到 5:1 甚至 10:1。
| 特性 | 行式存储(MySQL) | 列式存储(ClickHouse) |
|---|---|---|
| 数据读取 | 读取整行 | 只读需要的列 |
| 压缩效率 | 较低 | 极高(同列数据类型一致) |
| 适用场景 | OLTP(频繁增删改) | OLAP(批量查询、聚合分析) |
| 典型延迟 | 毫秒级(单行) | 秒级(大范围扫描) |
3.1.2 核心架构组件
ClickHouse 的架构其实不复杂,核心就几个角色。我画了张图帮你理清关系。
核心要点:ClickHouse 的查询速度来源于「列式存储 + 向量化执行 + 稀疏索引」三驾马车。缺一个,性能都会大打折扣。
3.2 单机部署
部署 ClickHouse 其实很简单。我建议你直接用官方 RPM 包或者 Docker,省去编译的麻烦。这里我以 CentOS 7 为例,走一遍流程。
3.2.1 安装步骤
- 添加官方仓库
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://packages.clickhouse.com/rpm/clickhouse.repo
- 安装服务端和客户端
sudo yum install -y clickhouse-server clickhouse-client
- 启动服务
sudo systemctl start clickhouse-server
sudo systemctl enable clickhouse-server
- 验证连接
clickhouse-client --host 127.0.0.1 --port 9000
如果看到 ClickHouse server version 24.x.x 的提示,说明部署成功。
小技巧:我第一次部署时忘了开防火墙端口,结果客户端连不上。记得检查 9000(TCP)和 8123(HTTP)端口是否开放。
3.2.2 配置文件快速了解
ClickHouse 的主配置文件在 /etc/clickhouse-server/config.xml。我个人习惯先改两个地方:
- 数据存储路径:默认在
/var/lib/clickhouse,如果磁盘空间不够,可以改到挂载盘。 - 监听地址:默认只监听
127.0.0.1,如果想远程访问,改成0.0.0.0。
注意:生产环境不要直接暴露 ClickHouse 到公网。建议通过内网访问,或者用 Nginx 做反向代理加认证。
3.3 建表与基本查询
好,服务跑起来了,咱们开始建表。ClickHouse 的建表语法和 MySQL 有点像,但细节上差别很大。
3.3.1 创建数据库
CREATE DATABASE IF NOT EXISTS olap_demo;
3.3.2 创建 MergeTree 表
MergeTree 是 ClickHouse 最核心的表引擎。我建议你一开始就用它,别碰那些乱七八糟的引擎。
CREATE TABLE olap_demo.order_events (
event_time DateTime,
user_id UInt32,
order_id UInt64,
amount Float64,
status String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_time, user_id);
这里有几个关键点:
- PARTITION BY:按月分区,查询时能快速跳过无关分区。
- ORDER BY:排序键,也是稀疏索引的依据。查询条件尽量匹配排序键的前缀。
避坑指南:我曾经在项目中把 ORDER BY 设成了 (user_id, event_time),结果按时间范围查询时索引失效,全表扫描。后来改成 (event_time, user_id),查询速度提升了 10 倍。排序键的顺序很重要!
3.3.3 插入数据
ClickHouse 支持批量插入,单条插入性能很差。我一般用 CSV 文件批量导入。
INSERT INTO olap_demo.order_events VALUES
('2024-01-15 10:30:00', 1001, 50001, 299.00, 'completed'),
('2024-01-15 10:35:00', 1002, 50002, 159.00, 'pending'),
('2024-01-16 09:20:00', 1001, 50003, 89.90, 'completed');
3.3.4 基本查询
来几个典型查询,感受一下 ClickHouse 的威力。
查询某天所有订单
SELECT * FROM olap_demo.order_events
WHERE event_time >= '2024-01-15' AND event_time < '2024-01-16';
按用户统计消费总额
SELECT user_id, sum(amount) AS total_amount
FROM olap_demo.order_events
GROUP BY user_id
ORDER BY total_amount DESC;
按月统计订单数量
SELECT toYYYYMM(event_time) AS month, count(*) AS order_count
FROM olap_demo.order_events
GROUP BY month
ORDER BY month;
你想想看,同样的查询在 MySQL 里跑百万级数据可能就要几秒,ClickHouse 在亿级数据下依然能秒出。这就是列式存储 + 向量化执行的优势。
3.3.5 常用函数速览
| 函数 | 作用 | 示例 |
|---|---|---|
count() |
计数 | count(DISTINCT user_id) |
sum() |
求和 | sum(amount) |
avg() |
平均值 | avg(amount) |
uniq() |
近似去重计数 | uniq(user_id) |
toYYYYMM() |
提取年月 | toYYYYMM(event_time) |
经验之谈:ClickHouse 的 uniq() 函数用的是 HyperLogLog 算法,误差在 1% 左右,但速度极快。如果要求精确去重,用 count(DISTINCT ...),但性能会差一些。我在做用户画像时,通常用 uniq() 做快速估算,精确值留到离线计算。