文章封面图: ClickHouse 是什么:特点与一步步安装(2026 指南)

ClickHouse 是什么:特点与一步步安装(2026 指南)

发布于:

阅读时间: 5 min

主题: 技术

作者: Leandro Valencia

#ClickHouse#OLAP#数据分析#开源#自托管#SQL

给独立开发者的 ClickHouse 实践指南:它是什么、为什么这么快、关键特性,以及如何用 curl 或 Docker 在 5 分钟内装好。

目录

ClickHouse 是什么(不讲营销话术)

ClickHouse 是一个开源的列式分析数据库,用 C++ 写成,设计目的是以"看起来像测量误差"的速度对大数据量做聚合查询。

关键词是分析。在数据库行话里,这指的是 OLAP(联机分析处理),与 PostgreSQL 或 MySQL 做的 OLTP(联机事务处理)相对。

这个区别比看起来重要:

OLTP(Postgres、MySQL、SQLite) 被优化来非常快地读写单行。"给我用户 4821"。"更新这个订单的状态"。每秒数千次小操作,带事务和强一致性。

OLAP(ClickHouse、DuckDB、BigQuery) 被优化来读很多行但很少列。"给我过去 90 天按国家汇总的收入"。它不在乎某具体一行,它在乎的是扫掉数百万行并压成一个数。

如果你想看这个类别的完整背景——历史、用例和厂商地图——我在 OLAP 数据库是什么 里展开讲过。

让它成为可能的技术关键是列式存储。一个按行的数据库在磁盘上这样存数据:

[id:1, país:ES, ingresos:42, timestamp:...][id:2, país:MX, ingresos:17, timestamp:...]

你要累加 ingresos 列,就得把所有东西都读出来,包括你并不关心的 paístimestamp。ClickHouse 把每一列存到自己的文件里:

ingresos: [42, 17, 88, 12, ...]
país:     [ES, MX, ES, AR, ...]

要累加 ingresos,你只读这个文件。如果你的表有 40 列,而你的查询用 2 列,你只读了 5% 的数据。魔法就是从这开始的,剩下的一切都在放大它。

为什么这么快:四个真正的原因

1. 残酷的压缩。 当你把同类型的值放在一起、而且还排好序,模式就会重复,压缩算法变得非常高效。ClickHouse 文档给出的压缩比,从自由文本的 2x 一路到排好序的低基数列的 1,000x 以上。磁盘上字节更少 = I/O 更少 = 查询更快。

2. 稀疏主索引。 ClickHouse 不为每行建一条 B 树索引。它建的是稀疏索引:每 8,192 行(一个"颗粒")一条。这意味着每 TB 数据只要几 MB 索引,轻松装进内存。代价是 ClickHouse 永远不查精确的一行,而是查它所在的块。对分析来说,这是完全正确的取舍。

3. 向量化 + 并行引擎。 操作不是逐行执行,而是用 SIMD 指令在列的块上执行。而且会激进地并行到所有可用核心。在 8 核笔记本上你就感觉得到差别;在 64 核服务器上是另一个级别。

4. 读写分离。 插入会创建新的"part",在后台合并(LSM 风格的架构)。插入不阻塞查询,查询也不阻塞插入。

你应该知道的主要特性

这些才是真正会改变你项目设计的东西,而不是 datasheet 上的完整清单。

单个二进制、零依赖

这听起来像个小细节,但对小项目来说很可能是最重要的一点。ClickHouse 以单个可执行文件分发,内含服务器、客户端和本地模式。没有 JVM、没有强制的 ZooKeeper、没有编排器。你下载一个文件,运行它,你就有一个分析数据库。

把它和启动 Apache Druid 对比一下——Druid 需要六种不同的进程。对单打独斗的独立开发者来说,这个差别决定了项目是上线还是死在 docker-compose.yml 里。

clickhouse-local:对文件跑 SQL、不用起服务器

你可以直接从磁盘或 URL 查 CSV、Parquet 或 JSON,不用导入任何东西,也不用起服务器:

./clickhouse local --query "
  SELECT country, count() AS visitas
  FROM file('logs.csv', CSVWithNames)
  GROUP BY country
  ORDER BY visitas DESC
  LIMIT 10
"

这能替代用 pandas 写的快速探索脚本,在大文件上明显更快。

增量物质化视图

这是最改变我构建方式的一个特性。ClickHouse 的物质化视图不是会刷新的缓存:它是一个插入触发器。每次你往源表插入,这个块上的聚合就被算出来、写到目标表里。

结果是:你把成本从查询时刻挪到了插入时刻。你的 dashboard 查的是一张 4,000 行的预聚合表,而不是 4 亿行的原始数据。我在进阶指南里深入讲,但先把这个概念记住。

完整 SQL 支持加上真 JOIN

多年来对 ClickHouse 的标准批评是"JOIN 差"。现在已经站不住脚了。它支持所有标准 JOIN 类型,有一个用列统计来重排 JOIN 的优化器,还加了标准 SQL 里没有的类型,比如 ASOF JOIN(按最接近的时间点而不是精确值来连接),这对时序和金融数据是纯金。

它还用数百个分析函数扩展 SQL:uniqExactquantileTDigestwindowFunnelsequenceMatch。在 Postgres 里要写 60 行 CTE 的东西,在这里是一个函数。

兼容 70+ 种格式和数据湖

它能读写 Parquet、Iceberg、Delta Lake、Avro、Protobuf、各种 JSON。它能直接查 S3 上的文件而不摄入。这意味着它不会把你锁住:哪天你想迁移,一条语句数据就以 Parquet 出来了。

不爆 schema 的 JSON

你可以用原生 JSON 类型摄入半结构化数据,它内部以列式存储。不用提前设计完美 schema 就能摄入形状各异的事件,而仍然保有列式数据库的性能。

我的看法:什么时候 ClickHouse 是对的选择、什么时候不是

这是大多数教程会让你失望的地方,因为它们是想卖你东西的人写的。

ClickHouse 是好主意,如果:

你有一张停不下来地增长的事件表——产品分析、日志、指标、点击、IoT 遥测——而你的查询是对时间范围做聚合。这是经典用例,也是它会像魔法的地方。

你在做面向用户的分析:你产品里的一个 dashboard,很多客户同时查。ClickHouse 以毫秒级延迟处理高并发,而这正是 Snowflake 这类数仓变贵的地方。

你的 BigQuery 或 Snowflake 账单让你焦虑。一台每月 30-60 欧的 ClickHouse 专用服务器,能扛下在按消费计费的数仓上贵一个数量级的负载。对 bootstrap 的项目来说这不是优化,是可行和不可行的差别。

ClickHouse 是坏主意,如果:

你需要频繁按行更新和删除。ClickHouse 在这方面进步很大(轻量 mutation、ReplacingMergeTree、轻量删除),但它仍然是一个为"写一次、读多次"设计的引擎。如果你的负载是 CRUD,用 Postgres。

你需要多表 ACID 事务。事务支持有限,而且不是项目目标。别把订单状态放在这里。

你的数据少于几百万行。50 万行的表,Postgres 加一个像样的索引就能毫秒级回答。加 ClickHouse 是没有收益的运维复杂度。小数据集的正确答案是不换数据库。

你的团队是 0 个人,而你已经要维护三个服务。这是又一个要监控、备份、升级的服务。只有性能痛点是真实的时候才值得,而不是预期中的时候。

我的实操规则:如果在你最大的表上跑一个 GROUP BY 在 Postgres 里要超过 5 秒,而这条查询是你产品的核心,就是时候看 ClickHouse 了。 在此之前,不要。

还有一点补充:对我最有效的模式不是替换 Postgres,而是把两者放在一起。Postgres 继续做事务的真相来源——用户、订单、配置——而 ClickHouse 通过 CDC 或复制任务接收事件表的副本。各自做各自擅长的事。

ClickHouse 一步步安装

来点实操。根据你的需要,有三条路。

方式 1:用 curl 快速安装(macOS、Linux、FreeBSD)

最直接的办法。下载适合你系统的二进制:

curl https://clickhouse.com/ | sh

在 Linux 和 macOS 上,这还会把 clickhousectl(别名 chctl)装到 ~/.local/bin,这是用来管理多个本地版本、在后台启动服务器、与 ClickHouse Cloud 连接的官方 CLI。

如果你只想要二进制、不要管理 CLI:

curl https://clickhouse.com/ | CLICKHOUSE_ONLY=1 sh

给 macOS 用户的提示: 如果 Gatekeeper 抱怨无法验证二进制的开发者,到"系统设置 → 隐私与安全性"里批准运行。这是 macOS 对未签名二进制的正常行为。

启动服务器:

./clickhouse server

在另一个终端里启动客户端:

./clickhouse client

你应该会看到类似这样的输出:

ClickHouse client version 24.5.1.117 (official build).
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 24.5.1.

local-host :)

数据存在当前目录,服务器重启后还在。

方式 2:Docker(如果你已经在用容器,推荐)

当项目已经有 docker-compose.yml 时,这是我的默认选择,因为它不弄脏系统、环境可复现。

docker run -d \
  --name clickhouse \
  -p 8123:8123 \
  -p 9000:9000 \
  -v clickhouse_data:/var/lib/clickhouse \
  --ulimit nofile=262144:262144 \
  clickhouse/clickhouse-server

这两个端口很重要,一开始每个人都搞混:

  • 8123 是 HTTP 接口。大多数 Web 客户端、BI 工具和 curl 用它。
  • 9000 是原生 TCP 协议。更快更紧凑;clickhouse-client 和原生驱动用它。

--ulimit nofile 这个参数实际上不可省:ClickHouse 会打开很多文件描述符,不提高上限的话负载下会出莫名其妙的错误。

写成 docker-compose.yml:

services:
  clickhouse:
    image: clickhouse/clickhouse-server
    container_name: clickhouse
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - clickhouse_data:/var/lib/clickhouse
      - ./config.d:/etc/clickhouse-server/config.d
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    environment:
      CLICKHOUSE_USER: creacosas
      CLICKHOUSE_PASSWORD: cambia_esto
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1

volumes:
  clickhouse_data:

连到容器里的客户端:

docker exec -it clickhouse clickhouse-client --user creacosas --password cambia_esto

方式 3:什么都不装

如果你只想试一下语法、感受速度,官方 playground 里有已加载的真实数据集,并能在浏览器里执行查询。零摩擦。

你的第一批查询:从零到真实数据

我们来做点接近真实场景的东西:一个网站的事件表。

建库建表

CREATE DATABASE creacosas;

USE creacosas;

CREATE TABLE eventos
(
    fecha       Date,
    timestamp   DateTime,
    usuario_id  UInt32,
    evento      LowCardinality(String),
    pais        LowCardinality(String),
    url         String,
    duracion_ms UInt32
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(fecha)
ORDER BY (evento, fecha, usuario_id);

这里有三处值得展开的决策,因为它们是ClickHouse 式的决策:

ENGINE = MergeTree 是默认表引擎,90% 的情况你都用它。整个 MergeTree 家族(ReplacingMergeTreeSummingMergeTreeAggregatingMergeTree)共享同一个底座,在 part 合并上加了额外行为。

ORDER BY 是你整个 schema 里最重要的决策。它定义了数据在磁盘上的物理顺序,默认也是主键。规则:把你最常用来过滤、且基数更低的列放前面。我们几乎总是按事件类型和日期过滤,所以它们在前。这里搞错,是"ClickHouse 慢"的头号原因。

LowCardinality(String) 是一个会做字典编码的类型。如果一列的不同值少于约 10,000 个(国家、事件类型、套餐名),把它包进 LowCardinality 会减少存储并明显加速过滤。这是一个几乎没人一开始会用的免费收益。

插入测试数据

我们来生成 1000 万行合成数据,好有东西玩:

INSERT INTO eventos
SELECT
    toDate('2026-01-01') + (number % 220)                       AS fecha,
    toDateTime(fecha) + (number % 86400)                        AS timestamp,
    (number % 50000) + 1                                        AS usuario_id,
    ['pageview','click','signup','purchase'][(intHash32(number) % 4) + 1]      AS evento,
    ['ES','MX','AR','CO','CL','US'][(intHash32(number + 7) % 6) + 1]           AS pais,
    concat('/pagina/', toString(number % 500))                  AS url,
    (number % 5000) + 100                                       AS duracion_ms
FROM numbers(10000000);

numbers() 函数即时生成行:这是 ClickHouse 里不下载任何东西就能造测试数据集的标准办法。我对类别列不用 number % N 而用 intHash32(),是因为如果两列都用以有公因子的模数(比如 4 和 6),它们会相关,结果会人为地均匀。

查询

SELECT
    pais,
    evento,
    count()                         AS total,
    round(avg(duracion_ms))         AS duracion_media,
    uniqExact(usuario_id)           AS usuarios_unicos
FROM eventos
WHERE fecha >= '2026-03-01'
  AND evento = 'purchase'
GROUP BY pais, evento
ORDER BY total DESC;

注意客户端输出的最后一行。它告诉你处理了多少行、读了多少字节、速度是多少:

6 rows in set. Elapsed: 0.021 sec. Processed 1.14 million rows, 8.32 MB (54.28 million rows/s., 396.19 MB/s.)

那个数字——总共 1000 万行里的"processed 1.14 million rows"——才是关键。ClickHouse 只读了表的 11%,因为 ORDER BY (evento, fecha, ...) 让它跳过了其他所有东西。优化 ClickHouse 几乎总是围绕降低这个数字。

一个小技巧:看磁盘上的实际大小

SELECT
    table,
    formatReadableSize(sum(data_compressed_bytes))   AS comprimido,
    formatReadableSize(sum(data_uncompressed_bytes)) AS sin_comprimir,
    round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE database = 'creacosas'
GROUP BY table;

系统表(system.columnssystem.partssystem.query_log)是 ClickHouse 最赞的东西之一:引擎的全部内省都是可查询的 SQL。

你会犯的错(我都犯过)

一行一行地插入。 ClickHouse 讨厌小插入。每次 INSERT 都在磁盘上创建一个新"part",之后还要合并。一千次单行插入会产生一千个 part,服务器会因 Too many parts 报错而窒息。每次至少 1,000 行批量插入——理想是 10,000 到 100,000——或者开启 async_insert=1 异步插入。

习惯性地用 Nullable 每个 Nullable 列都会多加一列掩码、并禁用一些优化。如果你能用哨兵值(0、空字符串、1970-01-01),就用。

把高基数列放在 ORDER BY 第一位。 如果你把 usuario_id 或 UUID 放前面,你就破坏了压缩和跳块能力。低基数在前,永远如此。

期望 UPDATE 像 Postgres 那样工作。 能用,但操作很重。如果你的设计依赖不断更新行,请重新考虑设计(或重新考虑数据库)。

结论

ClickHouse 不是通用数据库,而这正是它的价值:它把一件事——对大体量做聚合——做得比几乎所有人都好,而且以相对于其能力而言小得离谱的运维占用做到。一个二进制、零依赖,从笔记本到数百节点用同一个引擎跑。

对单打独斗的独立开发者来说,最后这一点才是决定性的。你不需要一个数据团队来运维 VPS 上的 ClickHouse。你需要的是好好理解 ORDER BY,以及不要逐行插入。

如果你是从 Postgres 过来的、dashboard 奄奄一息,那就给它一个下午。初始学习曲线是几个小时而不是几周,而性能跃升是那种不用测量都能感觉到的。

在本系列的下一批文章里,我们深入装好之后的两个问题:


常见问题

ClickHouse 免费吗?

引擎是 Apache 2.0 开源的,你可以无成本、无功能限制地自托管。ClickHouse Cloud 是付费的托管服务,采用计算与存储分离、按消费计费的模型,无活动时可以缩到零。

ClickHouse 能替代 PostgreSQL 吗?

一般情况下不能。它们是干不同活的工具:Postgres 做事务和应用状态,ClickHouse 做分析。常见模式是两个都用,Postgres 当真相来源,ClickHouse 接收事件数据。

多大数据量才值得?

作为实操参考,事件表从几千万行开始,差别就很明显了。少于几百万行的话,索引良好的 Postgres 就够了,运维也更简单。

在 Windows 上能跑吗?

可以,通过 Docker 或 WSL2,这是推荐路径。也可以通过 clickhousectl 安装。

该用什么表引擎?

90% 的情况用 MergeTree。需要按键去重就用 ReplacingMergeTree,作为聚合物质化视图的目标就用 SummingMergeTreeAggregatingMergeTree

相关文章

继续探索您可能感兴趣的相似内容

ClickHouse 是什么:特点与一步步安装(2026 指南)