ClickHouse 基础知识

ClickHouse 适合回答“过去一周每小时有多少次请求”“不同事件类型的用户数是多少”这类分析问题。它可以持续接收新数据,并在大量记录上执行筛选和聚合。理解它时,先把它当作分析数据库,而不是把 MySQL 表原样搬过来。

列式存储解决了什么问题

按行存储适合取出或修改一条完整记录;分析查询通常只需要少数列,却要扫描很多行。ClickHouse 将同一列的数据放在一起,查询可以只读取所需列,同类型值也更容易压缩。这是它适合大范围聚合的原因之一,具体速度仍取决于表设计、数据量和查询条件。ClickHouse 对列式存储的介绍

从一张 MergeTree 表开始

MergeTree 是最常用的表引擎。下面用事件表记录时间、用户和事件类型:

CREATE TABLE events
(
    event_time DateTime,
    user_id UInt64,
    event_type LowCardinality(String)
)
ENGINE = MergeTree
ORDER BY (event_type, event_time);

INSERT INTO events (event_time, user_id, event_type) VALUES
    ('2026-10-01 10:00:00', 1, 'page_view'),
    ('2026-10-01 10:05:00', 2, 'page_view'),
    ('2026-10-01 10:10:00', 1, 'signup');

SELECT event_type, count() AS event_count
FROM events
GROUP BY event_type
ORDER BY event_count DESC;

这里的 ORDER BY 是写入后在数据 part 内的排序键,不是要求查询结果按此排序。没有显式指定 PRIMARY KEY 时,它也用作主键索引的键;这个主键不保证唯一性。查询结果要排序,仍须在 SELECT 中写 ORDER BY。ClickHouse 主键说明

排序键、part 和分区

MergeTree 写入的数据形成 part,后台会合并 part。每个 part 内按排序键排列,并建立稀疏索引:索引定位的是可能包含目标值的数据粒度,而不是逐行定位。因而,经常出现在筛选条件中的列适合优先考虑放入排序键;如果常按 user_id 查单个用户,而表按 event_type 排序,这个排序键未必能有效减少读取量。ClickHouse 对 part 与稀疏索引的说明

PARTITION BY 是另一回事。它把数据划分到不同分区,常用于按月管理和清理历史数据。不要为了每个查询条件都建分区;分区过细会产生大量 part。入门时可以先不分区,确认数据保留与清理需求后再决定。ClickHouse 入门常见问题

写入和修改数据时要注意什么

生产环境通常批量写入,避免大量极小的 INSERT 不断产生 part。无法在客户端攒批时,可以了解异步写入。上面的三行 INSERT 只是为了演示语法。ClickHouse 写入建议

ClickHouse 支持更新和删除,但其存储与索引设计首先服务于分析查询。如果业务需要频繁按主键修改单条记录、依赖唯一约束或处理事务性写入,通常应让事务数据库保存业务主数据,再把需要分析的数据同步到 ClickHouse。选择它之前,先确认主要查询是大范围筛选与聚合,还是高频单行读写。ClickHouse 更新机制说明