ClickHouse指南:50k星实时分析数据库,十亿级聚合查询实战

项目:ClickHouse/ClickHouse | 星数:50663 | 语言:C++ | 协议:Apache-2.0 | GitHub仓库

更新日期:2026-10-29 · 作者:CodeArts 编辑部

它不适合你,除非你的查询长这样

先把边界说清楚:ClickHouse 不是 OLTP 数据库。它不擅长单行更新、不支持真正的行级事务、点查性能不如 MySQL 或 Postgres。判断标准很明确——如果你日常跑的是 SELECT count() FROM events WHERE date >= ... GROUP BY ... 这类扫大量行、只取聚合结果的查询,而单行点查很少,那它就是当前性价比最高的选择之一。

50663 星、Apache-2.0、C++ 实现,单机就能跑出每秒数亿行的扫描速度,这是它最核心的卖点。

为什么快:三个设计决定

再加上高压缩率(列内相似值多,压缩比通常 5~10 倍),同样的数据量它占用的磁盘比行存库小得多,扫描也更快。

建表:MergeTree 是唯一正确答案

CREATE TABLE events (
    event_date  Date,
    event_time  DateTime,
    user_id     UInt64,
    country     LowCardinality(String),
    action      LowCardinality(String),
    amount      Decimal(18, 2)
) ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id)
TTL event_date + INTERVAL 180 DAY;

三个关键决策:

项原则
ORDER BY最常用的过滤列放前面,基数从低到高;改这个等于重建表
PARTITION BY按月最常用;分区过多(按天)会导致小文件过多
LowCardinality字符串枚举值必用,可显著降低存储与提升速度

ORDER BY 是最重要的决定:查询里常用来过滤的列必须出现在前面,否则稀疏索引无法裁剪数据块,等于全表扫描。这也是它和 MySQL 索引概念最大的不同——没有"随便加个索引"这回事。

常见查询写法

-- 每日活跃用户与成交额
SELECT event_date,
       uniqExact(user_id) AS dau,
       sum(amount) AS gmv
FROM events
WHERE event_date >= today() - 7
GROUP BY event_date
ORDER BY event_date;

uniqExact 精确去重,uniq 是 HyperLogLog 近似(快很多,误差约 0.5%)。做看板类指标时,uniq 通常完全够用,这是最好用的一个性能开关。

-- 漏斗/留存类,用窗口函数
SELECT user_id,
       minIf(event_time, action = 'view') AS t_view,
       minIf(event_time, action = 'buy')  AS t_buy
FROM events
WHERE event_date >= today() - 30
GROUP BY user_id
HAVING t_buy IS NOT NULL;

minIf 这类组合子函数非常顺手,很多行数统计、条件聚合都能一行写完,不必嵌套子查询。

物化视图:把聚合提前算好

CREATE MATERIALIZED VIEW events_daily_mv
ENGINE = SummingMergeTree
ORDER BY (event_date, action)
AS SELECT event_date, action,
          count() AS cnt, sum(amount) AS amt
   FROM events
   GROUP BY event_date, action;

物化视图在写入时增量计算,查询时直接读小表。注意它是"插入触发器",只对新写入的数据生效,历史数据要手动 backfill 一次。这是 ClickHouse 里最容易被误解的特性。

导入数据的三种常用方式

# 1. CSV/TSV 批量导入
clickhouse-client --query "INSERT INTO events FORMAT CSV" < data.csv

# 2. 从 MySQL 直接拉
INSERT INTO events SELECT * FROM mysql('host:3306', 'db', 'table', 'user', 'pass')

# 3. 从对象存储
INSERT INTO events SELECT * FROM s3('https://bucket/events/*.parquet', 'key', 'secret', 'Parquet')

批量导入每条几万到几十万行是合适粒度,单条 INSERT 是反模式——ClickHouse 每次写入都会产生一个新 part,小批写入会导致 part 数量暴涨,后台合并跟不上。

运维要点

部署选型

方式适用
单机 + Docker数据量 TB 以内、单点可接受
ClickHouse Keeper + 副本分片生产高可用,需要多节点
托管服务无运维团队

起步最省事的是单机:docker run -d -p 8123:8123 -p 9000:9000 clickhouse/clickhouse-server,然后 clickhouse-client 就能开始建表。真到需要分片时再上集群,过早做分布式只会增加运维负担。

选型对照

与 Doris、StarRocks 相比,ClickHouse 单表聚合更快、运维更简单,但多表 join 与并发点查较弱;与 Elasticsearch 相比,它在结构化聚合上快得多,代价是全文检索能力有限。如果你的核心需求是"海量明细数据上的快速聚合与报表",它几乎是默认答案。

常见问题

ClickHouse 能替代 MySQL 吗?

不能。它不支持真正的行级事务和频繁单行更新,点查性能也远不如 MySQL。两者是互补关系:MySQL 承担业务写入与点查,ClickHouse 承担分析与聚合查询,通过 CDC 或定时同步把数据导过去。

ORDER BY 建表后还能改吗?

不能直接修改,排序键决定了数据在磁盘上的物理布局,改动等于重建表。正确做法是新建表后用 INSERT SELECT 迁移数据,因此建表前务必想清楚最常用的过滤列。

为什么我的写入很慢?

最常见原因是单条或小批 INSERT 导致后台产生大量小 part,合并跟不上。应改为每批数万到数十万行的批量写入,并定期观察 system.parts 中 active part 数量。