ClickHouse 深度拆解:当列式存储开始吞掉数据仓库——从 MergeTree 引擎家族到向量化执行与 SharedMergeTree 云原生的完全指南(2026)
摘要:2026 年,ClickHouse 的年化收入突破 2.5 亿美元、剑指 IPO,Cloudflare、Uber、eBay、Spotify 都在用它扛每秒百万级的实时分析。本文从列式存储的本质讲起,逐层拆解 MergeTree 引擎家族、稀疏主键与向量化执行管线,再用一套可运行的实时事件分析系统(Materialized View + AggregatingMergeTree + 原生 JSON + SharedMergeTree)把理论落到代码,最后给出 15 条生产踩坑清单。读完你会明白:为什么在「一秒内扫完 10 亿行」这件事上,列式存储几乎是唯一答案。
一、背景介绍:为什么是 ClickHouse,为什么是现在
2009 年,Yandex(俄罗斯搜索引擎)为了保证自家的网站分析产品 Metrica 能在 PB 级数据上做实时报表,内部孵化了一个叫 ClickHouse 的引擎。2016 年它正式开源,2021 年成立独立的 ClickHouse Inc.。到 2026 年,它的年化收入已经突破 2.5 亿美元、同比增长三倍,正在为 IPO 铺路——这背后是一个正在发生的现实:列式分析数据库正在悄悄吞掉一大部分数据仓库的饭碗。
为什么是现在?因为三个趋势在 2026 年同时成熟:
- 数据爆炸但预算收紧。日志、埋点、IoT、Agent 轨迹每时每刻都在产生,但企业不再愿意为「只是想看个实时大盘」就采购一套按扫描量计费、动辄六位数月费的传统云数仓。
- 实时性成为标配。T+1 的离线报表已经不够看,业务要的是「事件发生后一秒内就能在仪表盘上看到」。
- 基础设施下沉到对象存储。S3 类廉价存储 + 分离式计算,让 ClickHouse 这类引擎可以用 1/10 的成本做到过去只有 Snowflake、Databricks 才敢承诺的事。
但要说清 ClickHouse 为什么快,得先回到一个老问题:为什么 MySQL、PostgreSQL 在 analytical 查询上这么拉?
答案不是它们写得差,而是它们的「基因」是行式存储,为 OLTP 而生。当你写 SELECT sum(amount) FROM orders WHERE create_date > '2026-01-01' 时,行式存储必须把每一行的所有列(订单号、用户、地址、备注……)都从磁盘读上来,再筛掉你根本不需要的 99% 字段。而列式存储只碰 amount 和 create_date 这两列——I/O 量级差出几个数量级。
这就是 ClickHouse 的全部起点:为「读多写少、宽表、聚合、扫描」而生的列式 OLAP 引擎。 但它又不是简单的「列存版 MySQL」,它有一整套自洽的引擎哲学(MergeTree)、一套向量化执行管线,以及一个在 2026 年彻底走向云原生的 SharedMergeTree。下面我们一层层拆。
先划清边界:ClickHouse 不是 OLTP 数据库,不支持跨行事务、不强一致、UPDATE/DELETE 是异步的「mutation」。它是分析引擎。拿它当业务主库用,是自找麻烦。
二、核心概念:把地基打牢
2.1 列式存储 vs 行式存储
假设有一张 events 表:
| user_id | event_type | duration | country |
|---------|------------|----------|---------|
| 101 | click | 12 | CN |
| 102 | view | 30 | US |
| 103 | click | 8 | CN |
行式存储(MySQL)在磁盘上大致是 101,click,12,CN | 102,view,30,US | 103,click,8,CN —— 一行挨着一行。列式存储则是把每一列单独存成一个文件:user_id: 101,102,103、event_type: click,view,click、duration: 12,30,8、country: CN,US,CN。
这带来两个质变:
- 只读需要的列。上面的
sum(duration)查询,列式存储只加载duration这一列文件,I/O 直接砍掉 75%。 - 压缩率高得离谱。同一列数据类型相同、取值往往集中(比如
country只有几十种、event_type几百种),字典编码(Dictionary)、游程编码(RLE)、Delta 编码能把它压到原来的 1/10 甚至更低。压缩不只是省空间,更省 I/O、更省内存带宽——这是 ClickHouse 快的底层燃料。
2.2 OLAP vs OLTP(一次说清边界)
| 维度 | OLTP(MySQL/PG) | OLAP(ClickHouse) |
|---|---|---|
| 读写模式 | 单行点查、小事务、读写均衡 | 批量写、海量扫描、聚合读 |
| 一致性 | 强一致、事务 ACID | 最终一致(异步 merge) |
| 数据形态 | 窄表、范式化 | 宽表、反范式、冗余换查询速度 |
| 典型查询 | WHERE id = ? | GROUP BY ... WHERE 时间范围 扫描亿级行 |
| 延迟目标 | 毫秒级写、强一致读 | 亚秒级扫十亿行 |
2.3 向量化执行(Vectorized Execution)
这是 ClickHouse 第二个速度来源。传统解释器式执行是「一行一行处理」(row-by-row):取一行→算→取下一行。CPU 分支预测、函数调用开销被放大,且完全用不上 SIMD。
ClickHouse 改成 按「块(Block)」处理:一个 Block 是一批列式数据(默认若干行,内部以列向量形式存在)。算子(函数、聚合、过滤)直接对整个列向量做操作,一次循环处理上千个值。这样既贴合 CPU 缓存(数据连续),又能让编译器自动向量化(SIMD),甚至手写 intrinsics。
伪代码对比:
// 行式(慢):每行一次函数调用,缓存不友好
for (row : rows) {
result[row] = f(row.a) + g(row.b);
}
// 向量化(快):对整个列向量批量运算
ColumnVector result = f(column_a) + g(column_b); // 一次处理整列
这不是 ClickHouse 的专利(MonetDB、DuckDB 都用),但 ClickHouse 把它和列存、稀疏索引结合起来,形成了乘数效应。
2.4 三个必须理解的概念:Part、Partition、Granule、Mark
这四个词不懂,后面全懵。
- Partition(分区):逻辑切分,
PARTITION BY toYYYYMM(event_date)会按月份把数据分到不同目录。它方便数据生命周期管理(整月直接 DROP)和裁剪,但不是性能主键——一个分区内的性能由 ORDER BY 决定。 - Part(数据部分):ClickHouse 写入不是原地改文件,而是每次 INSERT 生成一个不可变的 part。后台线程会异步把这些 part 合并(merge)成更大的 part。这就是它的 LSM 哲学:写永远只做追加,读在查询时按需合并。
- Granule(粒度):数据被切成固定行数的块,默认 8192 行一个 granule。它是「标记(mark)」的基本单位。
- Mark / 稀疏主键(sparse primary index):这是 ClickHouse 最反直觉也最精妙的设计。它不会为每一行建索引(那会让索引比数据还大)。而是每个 granule 记一个 mark,指向该 granule 在列文件中的偏移。主键(ORDER BY 的前缀,或单独指定的 PRIMARY KEY)就是这些 mark 的稀疏索引。
查询时:用主键做二分查找,定位到「可能包含目标数据的 granule 范围」,然后只读取这些 granule 对应的列数据块。主键越能匹配你的 WHERE 条件,跳过的 granule 越多,扫描量越小。这就是为什么 ORDER BY 是 ClickHouse 里最重要的一行 SQL——它决定了物理排列和稀疏索引的形态。
2.5 MergeTree 的 LSM 哲学
把 2.4 串起来:ClickHouse 借鉴了日志结构合并树(LSM-Tree)的思想——
- 写入极快:INSERT 只是 append 一个新的不可变 part,几乎零随机写。
- 读取靠合并:查询时多个 part 在引擎内部被「merge」成统一视图;后台再异步把小 part 物理合并成大 part,保持读取效率。
- 代价是「最终一致」:刚写入的数据可能还没合并,同一个主键的多版本会在查询时合并(或靠
FINAL/特定引擎去重)。这是用一致性换吞吐的取舍。
三、架构分析:ClickHouse 是怎么跑起来的
3.1 存储引擎家族(MergeTree 全家桶)
MergeTree 不是一种引擎,而是一整个家族。选错引擎,性能差十倍。
| 引擎 | 解决的问题 | 典型场景 |
|---|---|---|
MergeTree | 基础列存,追加+合并 | 日志、事件、时序原始表 |
ReplacingMergeTree(ver) | 合并时按版本去重 | CDC、维度表、覆盖写 |
SummingMergeTree | 合并时自动 sum 数值列 | 只关心汇总值的指标表 |
AggregatingMergeTree | 合并时合并聚合中间态 | 实时物化视图的核心 |
CollapsingMergeTree / VersionedCollapsingMergeTree | 用 sign 列实现行级「取消+新增」 | 频繁变更的状态(如订单状态流转) |
Replicated* | 上述各引擎 + Keeper 复制 | 高可用、多副本 |
SharedMergeTree | 共享存储(S3)+ Keeper 元数据 | 2026 云原生默认 |
重点讲三个:
AggregatingMergeTree——实时聚合的核武器。 普通 MergeTree 上做 SELECT count(DISTINCT user_id) FROM huge_table GROUP BY minute 每次都要扫全量。而 AggregatingMergeTree 让你把聚合的中间状态(不是最终结果)存进表里,配合 -State/-Merge 函数族和物化视图,写入时就算好、合并时再合并。查询直接读已经聚合好的小表。这是 ClickHouse 实时大盘的基石。
ReplacingMergeTree——优雅的覆盖写。 它没有「UPDATE」,而是「插入新版本,合并时保留最新」。非常适合维度表、CDC 接入。
SharedMergeTree——2026 的云原生答案。 传统 ReplicatedMergeTree 是「每个节点各自存一份数据、通过 Keeper 同步元数据」。SharedMergeTree 反过来:所有计算节点共享同一份 S3 上的数据文件,只有元数据在 Keeper 里。这意味着你可以按查询负载弹性扩缩计算节点,而不用搬数据——真正的存算分离。2026 年 ClickHouse Cloud 已把它作为默认引擎。
3.2 一次写入发生了什么(数据落盘全链路)
INSERT INTO events_raw VALUES (...);
这条语句落地后,在磁盘上大致生成:
store/
<partition>/
<part>/
event_type.bin # 列数据(压缩后的二进制)
event_type.mrk2 # 该列的 mark 文件(granule → 偏移)
user_id.bin
user_id.mrk2
...
primary.idx # 稀疏主键索引
checksums.txt # 校验和,保证 part 完整性
写入是 append 一个新的 part;ALTER TABLE ... UPDATE/DELETE 则是异步 mutation——它标记旧 part 失效、写新 part,后台再清理,所以 ClickHouse 的「改」和「删」不是即时的。
3.3 一次查询发生了什么(查询管线)
SQL 文本
→ Parser(语法解析成 AST)
→ Analyzer(语义分析、类型推导、别名解析)
→ Interpreter(生成执行计划)
→ QueryPipeline(构建处理器图)
→ Processors 并行执行(Source 读数据 → Transform 过滤/聚合 → Sink 输出)
关键点:Pipeline 是并行化的。ClickHouse 会按 max_threads 把扫描、聚合拆成多个 processor 并行跑,可以跨 part、跨 granule、跨线程。这就是为什么给它更多核,它真的能线性变快。
3.4 ClickHouse Keeper
Replicated* 和 SharedMergeTree 都需要一个协调者来存元数据、做选主、同步 replication log。ClickHouse Keeper 是用 C++ 重写的、兼容 ZooKeeper 协议的 Raft 实现——比 JVM 写的 ZK 更轻、更稳,避免了「ZK 抖动拖垮整集群」的经典事故。生产上建议 3 或 5 个 Keeper 节点。
3.5 云原生:SharedMergeTree + 对象存储
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Query Node │ │ Query Node │ │ Query Node │ ← 计算,可弹性扩缩
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└─────────┬───────┴─────────────────┘
│ 共享元数据
┌─────┴──────┐
│ Keeper │ ← 元数据、选主、replication log
└─────────────┘
│ 共享数据文件
┌─────┴──────┐
│ S3 / OSS │ ← 所有 part 的 .bin/.mrk,廉价、无限
└─────────────┘
对比经典 ReplicatedMergeTree(每节点存全量副本),SharedMergeTree 把存储彻底下放到对象存储,计算节点变成无状态——这正是它能在云上把成本压到传统数仓 1/10 的架构原因。
四、代码实战:从零搭一套实时事件分析系统
光讲架构没用,我们直接建一套「网站实时行为分析」系统:原始事件进 events_raw,实时聚合到分钟级大盘,支持去重 UV、原生 JSON 埋点、以及云原生部署。
4.1 建原始事件表(DDL)
CREATE TABLE events_raw
(
event_date Date DEFAULT toDate(event_time),
event_time DateTime64(3),
user_id UInt64,
event_type LowCardinality(String), -- 低基数字段用 LowCardinality 巨幅压缩+加速
page_url String,
country LowCardinality(String),
duration UInt32,
props String, -- 半结构化先存 String,后面用 JSON 列演示
-- 列级压缩 codec:时间类用 Delta+LZ4,数值更省
INDEX idx_country country TYPE set(100) GRANULARITY 4 -- 跳数索引加速点查
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date) -- 按月分区,便于生命周期管理
ORDER BY (event_type, event_date, user_id) -- 最关键:匹配最常见的过滤+分组
TTL event_date + INTERVAL 180 DAY -- 半年后自动清理,控制成本
SETTINGS index_granularity = 8192;
几点说明:
ORDER BY (event_type, event_date, user_id):把最常出现在WHERE和GROUP BY的字段放前面,稀疏索引才能最大化裁剪。LowCardinality(String):对event_type、country这种取值有限的字段,ClickHouse 内部用字典编码,压缩率和查询速度都能翻倍。TTL:把冷热分离自动化,避免表无限膨胀。
4.2 数据写入(Python)
用官方 clickhouse-connect 客户端,批量插入(永远用批量,别逐行 INSERT):
import clickhouse_connect
from datetime import datetime
client = clickhouse_connect.get_client(
host='clickhouse.example.com', port=8443,
username='writer', password='***', secure=True,
)
rows = [
{
'event_time': datetime(2026, 8, 13, 2, 39, 0),
'user_id': 101, 'event_type': 'click',
'page_url': '/p/123', 'country': 'CN',
'duration': 12, 'props': '{"ab":"B","price":99}',
},
# ... 成千上万行
]
# column_names 顺序必须与表结构/字典键一致
client.insert(
'events_raw',
rows,
column_names=['event_time', 'user_id', 'event_type',
'page_url', 'country', 'duration', 'props'],
)
# 高吞吐场景可开启异步插入:客户端攒批后发,显著降低小批次数
# client.insert(..., settings={'async_insert': '1', 'wait_for_async_insert': '0'})
经验法则:单次插入 1k~100k 行、用 Native 或 JSONEachRow 格式、开 async_insert,单节点轻松扛住每秒数十万事件。
4.3 实时聚合:Materialized View + AggregatingMergeTree
这是整篇文章的「王炸」。物化视图在 ClickHouse 里不是传统意义上的视图,而是一个「插入触发器」:每当有数据写入源表,MV 就会把这批数据按定义好的聚合算好、写进目标表。
-- 目标表:存聚合中间态(注意是 -State 函数)
CREATE TABLE events_rollup_minute
(
event_date Date,
event_type LowCardinality(String),
minute DateTime,
pv AggregateFunction(count),
uv AggregateFunction(uniq, UInt64),
sum_duration AggregateFunction(sum, UInt32)
)
ENGINE = AggregatingMergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_type, minute);
-- 物化视图:写入 events_raw 时自动聚合到上面的表
CREATE MATERIALIZED VIEW events_mv
TO events_rollup_minute
AS SELECT
toDate(event_time) AS event_date,
event_type,
toStartOfMinute(event_time) AS minute,
count() AS pv,
uniqState(user_id) AS uv,
sumState(duration) AS sum_duration
FROM events_raw
GROUP BY event_type, toDate(event_time), toStartOfMinute(event_time);
-- 查询大盘:用 -Merge 把中间态合并成最终结果
SELECT
event_type,
sum(pv) AS pv,
uniqMerge(uv) AS uv,
sumMerge(sum_duration) AS total_duration
FROM events_rollup_minute
WHERE minute BETWEEN '2026-08-13 00:00:00' AND '2026-08-13 02:00:00'
GROUP BY event_type
ORDER BY pv DESC;
为什么这么设计?因为 events_rollup_minute 只有「每分钟 × 每种事件类型」一行,数据量比原始表小几个数量级。大盘查询扫的是这张小表,无论原始表涨到 100 亿行,实时查询始终亚秒级。这就是「预聚合 + 列存」的威力。
注意:MV 只对它创建之后写入的数据生效。历史数据要用
INSERT INTO events_rollup_minute SELECT ...手动回填。
4.4 去重与 CDC:ReplacingMergeTree
埋点常因重试、乱序产生重复事件。用 ReplacingMergeTree 按版本保留最新:
CREATE TABLE user_profile
(
user_id UInt64,
nickname String,
score UInt32,
updated_at DateTime
)
ENGINE = ReplacingMergeTree(updated_at) -- 合并时保留 updated_at 最大的行
ORDER BY user_id;
-- 读取最新态:用 FINAL(简单但贵)或 argMax(推荐,可利用索引)
SELECT user_id, argMax(nickname, updated_at) AS nickname,
max(updated_at) AS updated_at
FROM user_profile
GROUP BY user_id;
FINAL 会在查询时强制合并去重,写起来简单但扫描成本更高;生产上更推荐 argMax/max 这类聚合写法,能充分利用稀疏索引。
4.5 原生 JSON 列(2023+ 引入,2026 已成熟)
半结构化埋点不用再塞进 String 里手搓解析了,直接用 JSON 列,ClickHouse 会自动推断子字段类型并建立子列:
CREATE TABLE logs_json
(
ts DateTime,
service LowCardinality(String),
payload JSON
)
ENGINE = MergeTree
ORDER BY (service, ts);
INSERT INTO logs_json VALUES
(now(), 'api', '{"level":"info","latency_ms":12,"tags":["a","b"]}'),
(now(), 'api', '{"level":"warn","latency_ms":880,"tags":["x"]}');
-- 直接按 JSON 内字段查,性能接近原生列
SELECT payload.level, avg(payload.latency_ms) AS p99_proxy
FROM logs_json
WHERE service = 'api'
GROUP BY payload.level;
JSON 列对 schema 漂移(不同事件字段不同)极度友好,又保留了列存性能——是日志、埋点场景的甜点功能。
4.6 物化投影(Projection)自动加速
Projection 是「表的另一种物理排列」,查询优化器会自动判断是否用投影重写查询,对应用完全透明:
ALTER TABLE events_raw
ADD PROJECTION p_by_country
(
SELECT country, count()
FROM events_raw
GROUP BY country
);
-- 插入后让已有数据也物化该投影
ALTER TABLE events_raw MATERIALIZE PROJECTION p_by_country;
-- 下面这条按 country 聚合的查询,会自动命中投影,无需改 SQL
SELECT country, count() FROM events_raw GROUP BY country;
和物化视图的区别:Projection 是同一张表的内在结构、维护对查询透明;MV 是独立的物理表、需要自己管理。
4.7 Go 写入示例
package main
import (
"context"
"github.com/ClickHouse/clickhouse-go/v2"
"time"
)
func main() {
conn, err := clickhouse.Open(&clickhouse.Options{
Addr: []string{"clickhouse.example.com:9000"},
Auth: clickhouse.Auth{Database: "default", Username: "writer", Password: "***"},
})
if err != nil { panic(err) }
batch, _ := conn.PrepareBatch(context.Background(),
"INSERT INTO events_raw (event_time, user_id, event_type, country, duration)")
for i := 0; i < 10000; i++ {
_ = batch.Append(time.Now(), uint64(i), "click", "CN", uint32(i%100))
}
if err := batch.Send(); err != nil { panic(err) } // 一次攒批发送
}
五、性能优化:让 10 亿行飞起来
5.1 黄金法则:ORDER BY 决定生死
ClickHouse 里 80% 的性能问题,根子在 ORDER BY。它必须同时满足:
- 最常用于过滤(WHERE)的列放前面——让稀疏索引能最大裁剪;
- 高频 GROUP BY 的列尽量靠前;
- 高基数列(user_id)放最后——高基数列当索引前缀基本无法有效范围裁剪,徒增索引体积。
反例:ORDER BY (user_id, event_date) —— 用 WHERE event_date BETWEEN ... 时,因为 user_id 在前且高基,索引几乎失效,被迫全分区扫描。正例见 4.1。
5.2 分区不要过细
PARTITION BY toYYYYMM(date) 是甜点;若按天甚至按小时分区,会产生海量小 part,merge 压力爆炸、元数据膨胀。除非数据有明确的「整块丢弃」需求,否则月/周级分区更稳。
5.3 物化视图与投影是「以空间换时间」的正解
热查询(大盘、告警)一律走预聚合(AggregatingMergeTree + MV)或 Projection,别让业务查询直接扫原始大表。
5.4 关键 Settings
SET max_threads = 16; -- 一般等于核数
SET max_memory_usage = 10000000000; -- 单查询内存上限(约 10GB)
SET use_uncompressed_cache = 1; -- 热列缓存到内存,repeat 查询飞快
SET max_bytes_before_external_group_by = 5e9; -- 聚合超内存则落盘,避免 OOM
SET max_bytes_before_external_sort = 5e9; -- 排序超内存则落盘
SET optimize_throw_if_no_new_data = 1; -- merge 无新数据时报错,便于运维感知
5.5 TTL 与生命周期
- 行级 TTL:
TTL event_date + INTERVAL 180 DAY DELETE—— 自动清旧数据。 - 列级 TTL:
duration TTL event_date + INTERVAL 30 DAY—— 热期保留明细、冷期只留聚合列,进一步省成本。
5.6 跳数索引(Skip Index)
主键之外的点查,靠跳数索引加速。常见:minmax(范围)、set(枚举)、bloom_filter(字符串包含)、ngrambf_v1(LIKE 模糊)。例如 4.1 里的 INDEX idx_country country TYPE set(100) 能让 WHERE country = 'CN' 跳过不含 CN 的 granule。
5.7 数据类型与编码细节
LowCardinality(String)用于低基数字符串。- 时间字段用
DateTime64,配上CODEC(Delta, ZSTD)压缩率极高。 - 单调序列(自增 id、时间戳)用
Delta编码。 - 避免
String存枚举,用Enum8/Enum16或LowCardinality。 - 永远
SELECT具体列,别SELECT *——列存的最大优势就是只碰需要的列。
5.8 十五一条生产踩坑清单
- ORDER BY 选错——先想清楚 80% 查询的 WHERE/GROUP BY 再建表。
- 分区过细——按天/小时分区制造海量小 part,merge 崩溃。
- 逐行 INSERT——必须用批量,单批 1k~100k 行。
- 用 FINAL 当常态——
FINAL强制去重很贵,能用argMax/max就别用。 - MV 不回填——MV 只对创建后写入生效,历史数据要手动
INSERT ... SELECT。 - 忽视 Keeper——Replicated/Shared 引擎依赖 Keeper,Keeper 挂了集群不稳,至少 3 节点。
- 内存不设上限——
max_memory_usage不设,一个大查询拖垮全节点。 - TTL 忘配——表无限膨胀,存储成本失控。
- SELECT *——白白扫描所有列,I/O 翻倍。
- 把 ClickHouse 当 OLTP——拿它做事务、高频点更新,自寻死路。
- 乱用 Nullable——
Nullable列多存一个 null 标记文件、拖慢查询,能用默认值就别 nullable。 - 跳数索引乱加——每个索引都占空间、写时开销,只为真正点查的列加。
- 聚合超大表不落盘——
max_bytes_before_external_group_by不设,OOM 频发。 - 生产用 default 用户——必须建专用账号、分库授权、开 TLS。
- 不看系统表——
system.query_log、system.merges、system.performance_counters是你定位慢查询的金矿,平时就要接监控。
六、总结展望
ClickHouse 赢在哪
当你需要「在一秒内扫完 10 亿行、做实时聚合、成本还要可控」时,ClickHouse 几乎是当前开源世界的最优解之一。它的护城河是三位一体:列式存储(省 I/O)+ 向量化执行(榨干 CPU)+ MergeTree 引擎家族(把聚合/去重/CDC 变成引擎内建能力)。配合 2026 年成熟的 SharedMergeTree 存算分离,它已经能从单机嵌入式一路吃到 PB 级云原生。
它和谁竞争,边界在哪
- vs DuckDB:DuckDB 是「进程内嵌入式分析引擎」,单机、零运维、配合 Python/Pandas 极佳;ClickHouse 是「分布式服务级分析数据库」。前者在笔记本上分析几 GB 文件,后者在集群上扛每秒百万事件。两者不是替代,是互补。
- vs StarRocks / Doris:都是 MPP 向量化 OLAP,StarRocks 在多维星型模型、高并发上更激进;ClickHouse 在宽表、日志、灵活 schema 上更自在。
- vs Snowflake / Databricks:传统云数仓胜在生态、SQL 兼容、企业治理;ClickHouse 胜在成本 1/10、实时性、自托管自由。
- vs Elasticsearch:日志检索 ES 仍是王者,但纯「聚合分析」场景 ClickHouse 更便宜更快,很多团队已用 ClickHouse 替代 ES 做日志分析。
2026 的关键趋势
- SharedMergeTree 成为云默认——存算分离彻底主流化。
- 湖仓一体打通——ClickHouse 对 Apache Iceberg / Hudi / Delta 的集成越来越深,能直接查对象存储上的开放表格式,不必把数据搬进 ClickHouse。
- 原生 JSON 成熟——半结构化数据的列存性能问题基本解决。
- Lightweight Delete——改进的点删性能,让「删除」不再那么异步昂贵。
- 向量检索实验特性——AI 时代,把 embedding 检索和 SQL 分析合二为一,正在路上。
什么时候选它,什么时候别
选它:日志/埋点/时序/IoT 分析、实时大盘与告警、广告/推荐的效果归因、需要 SQL 又要求亚秒级扫亿级行、想要可控成本的自建分析平台。
别选它:强事务业务主库、复杂多表join的TP系统、需要完整ACID和行级锁的场景、团队没有一点运维能力且不愿上云托管版。
列式存储从来不是银弹。但当业务的生死线变成「数据刚产生,仪表盘就要动」时,ClickHouse 用一整套自洽的工程哲学告诉你:把写做简单、把读做极致、让存储下沉,分析这件事本可以又快又便宜。 这,就是 2026 年它还在一路狂奔的根本原因。
本文代码示例基于 ClickHouse 24.x/25.x 稳定语法,2026 年 SharedMergeTree、原生 JSON、Lightweight Delete 均已可在生产使用;具体参数请结合你的版本与集群规模调整。