1. 表引擎概述
ClickHouse 的表引擎(Table Engine)定义了数据如何存储、读取和写入。选择合适的引擎是 ClickHouse 设计的核心决策之一。
引擎分为几大类别:
- MergeTree 家族:生产环境的主力,支持数据按主键排序、分区、复制
- Log 家族:轻量级,适合快速写入小表或临时数据
- 集成引擎:连接外部系统(Kafka、MySQL、PostgreSQL、S3 等)
- 特殊引擎:内存表、分布式表、视图等
2. MergeTree 引擎家族
MergeTree 是 ClickHouse 最核心和最常用的引擎。所有 MergeTree 变体共享相同的底层存储机制(有序、分区、稀疏索引),但在数据合并时有不同的行为。
2.1 MergeTree(基础引擎)
CREATE TABLE events (
event_time DateTime,
user_id UInt64,
event_type String,
value Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_time, user_id);
特点:
- 数据按 ORDER BY 列排序存储
- 支持数据分区(PARTITION BY)
- 后台异步合并小 part
- 不支持去重或聚合——纯存储引擎
适用场景:原始数据存储,不需要去重或自动聚合的场景。
2.2 ReplacingMergeTree(去重)
当需要保证同一主键只有最新一条记录时,使用 ReplacingMergeTree:
CREATE TABLE user_latest_state (
user_id UInt64,
status String,
last_login DateTime,
score UInt32
) ENGINE = ReplacingMergeTree(last_login)
ORDER BY user_id;
INSERT INTO user_latest_state VALUES
(1, 'active', '2024-01-01 10:00:00', 100),
(1, 'inactive', '2024-01-02 11:00:00', 200),
(2, 'active', '2024-01-01 09:00:00', 50);
-- 合并后,user_id=1 只保留 last_login 最新的记录
关键理解:去重发生在后台合并时,不是写入时。所以刚插入后立刻查询仍可能看到重复:
-- 合并前
SELECT * FROM user_latest_state WHERE user_id = 1;
-- 可能返回 2 条(未合并)
-- 强制最终一致性查询
SELECT * FROM user_latest_state FINAL WHERE user_id = 1;
-- 只返回最新的一条(强制合并)
适用场景:用户状态表(只关心最新状态)、配置表、最近一笔交易记录。
2.3 SummingMergeTree(自动求和)
自动对同一主键的数值列进行求和:
CREATE TABLE event_counters (
event_date Date,
event_type String,
page_url String,
view_count UInt64,
click_count UInt64
) ENGINE = SummingMergeTree()
ORDER BY (event_date, event_type, page_url);
-- 插入两条相同主键的记录
INSERT INTO event_counters VALUES ('2024-01-01', 'page_view', '/home', 10, 2);
INSERT INTO event_counters VALUES ('2024-01-01', 'page_view', '/home', 20, 3);
-- 合并后:view_count = 30, click_count = 5
适用场景:计数器、统计报表的预聚合层。
2.4 AggregatingMergeTree(聚合状态)
与 SummingMergeTree 不同,AggregatingMergeTree 可以处理任意聚合函数(uniq、min、max、groupArray 等)。
CREATE TABLE user_metrics (
event_date Date,
user_id UInt64,
total_views AggregateFunction(sum, UInt64),
unique_pages AggregateFunction(uniq, String),
max_session_duration AggregateFunction(max, UInt32)
) ENGINE = AggregatingMergeTree()
ORDER BY (event_date, user_id);
-- 插入需要使用 -State 后缀
INSERT INTO user_metrics SELECT
today(),
user_id,
sumState(toUInt64(1)) AS total_views,
uniqState(page_url) AS unique_pages,
maxState(session_duration) AS max_session_duration
FROM raw_events
GROUP BY user_id;
-- 查询时使用 -Merge 后缀
SELECT
event_date,
sumMerge(total_views) AS views,
uniqMerge(unique_pages) AS pages,
maxMerge(max_session_duration) AS max_duration
FROM user_metrics
GROUP BY event_date;
适用场景:需要多维度预聚合的实时指标系统。
2.5 CollapsingMergeTree(状态变更追踪)
通过符号位记录状态变更:
CREATE TABLE account_changes (
user_id UInt64,
balance Float64,
change_time DateTime,
sign Int8 -- +1 表示新增,-1 表示删除/反转
) ENGINE = CollapsingMergeTree(sign)
ORDER BY (user_id, change_time);
-- 记录状态变化
INSERT INTO account_changes VALUES (1, 100.0, '2024-01-01 10:00:00', 1);
INSERT INTO account_changes VALUES (1, 100.0, '2024-01-01 10:00:00', -1); -- 撤销
INSERT INTO account_changes VALUES (1, 150.0, '2024-01-01 11:00:00', 1); -- 新状态
-- 合并后:第一条和第二条抵消,只保留第三条
适用场景:需要追踪状态变更历史的场景,如账户余额变动、库存变化。
2.6 VersionedCollapsingMergeTree
CollapsingMergeTree 的增强版,支持版本号控制:
CREATE TABLE orders (
order_id UInt64,
amount Float64,
status String,
update_time DateTime,
sign Int8,
version UInt32 -- 版本号
) ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY order_id;
-- 同一版本号和符号的记录会相互抵消
2.7 GraphiteMergeTree
用于 Graphite 数据降采样(downsampling),按时间序列自动聚合数据点:
CREATE TABLE metrics (
path String,
time DateTime,
value Float64,
timestamp UInt32
) ENGINE = GraphiteMergeTree('graphite_rollup')
ORDER BY (path, time);
3. 复制引擎(Replicated*)
所有 MergeTree 引擎都有对应的 Replicated 版本,用于分布式高可用:
-- ReplicatedMergeTree
CREATE TABLE events_local (
event_time DateTime,
user_id UInt64
) ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/events', -- ZooKeeper 路径
'{replica}' -- 副本标识
)
ORDER BY (event_time, user_id);
复制引擎特性:
- 数据自动同步到所有副本
- 查询可以在任意副本执行,自动负载均衡
- 副本故障时自动切换
- 需要 ZooKeeper(或 ClickHouse Keeper)协调
4. 特殊引擎
4.1 内存引擎
-- Memory:纯内存存储,重启后数据丢失
CREATE TABLE temp_results (
id UInt64,
value Float64
) ENGINE = Memory;
-- Buffer:在内存中缓冲,定期刷写到目标表
CREATE TABLE events_buffer AS events
ENGINE = Buffer(default, events, 16, 10, 100, 10000, 1000000, 10000000, 100000000);
4.2 视图引擎
-- 物化视图(前文已详细介绍)
CREATE MATERIALIZED VIEW event_stats_mv ...
-- 普通视图(查询时展开,不存储数据)
CREATE VIEW recent_events AS
SELECT * FROM events WHERE event_time > now() - INTERVAL 7 DAY;
4.3 分布式引擎
-- 分布式表本身不存储数据,将查询路由到分片
CREATE TABLE events_distributed AS events_local
ENGINE = Distributed(
my_cluster, -- 集群名
default, -- 数据库名
events_local, -- 本地表名
rand() -- 分片键
);
4.4 集成引擎
-- Kafka 引擎:消费 Kafka 消息
CREATE TABLE kafka_queue (
user_id UInt64,
event_type String
) ENGINE = Kafka()
SETTINGS
kafka_broker_list = 'kafka:9092',
kafka_topic_list = 'events',
kafka_group_name = 'clickhouse_consumer',
kafka_format = 'JSONEachRow';
-- MySQL 引擎:查询 MySQL 表
CREATE TABLE mysql_users (
id UInt64,
name String
) ENGINE = MySQL('mysql-host:3306', 'mydb', 'users', 'user', 'password');
-- S3 引擎:查询 S3 上的文件
SELECT * FROM s3(
'https://bucket.s3.amazonaws.com/data/*.csv',
'CSV',
'id UInt64, name String'
);
-- URL 引擎:查询 HTTP 接口
SELECT * FROM url('https://api.example.com/data', JSONEachRow, 'id UInt64');
-- File 引擎:查询本地文件
SELECT * FROM file('data.csv', CSV, 'id UInt64, name String');
5. 引擎选择指南
| 场景 | 推荐引擎 | 说明 |
|---|---|---|
| 原始事件存储 | MergeTree | 最通用,无特殊合并逻辑 |
| 用户状态(最新) | ReplacingMergeTree | 自动保留最新记录 |
| 计数器/统计 | SummingMergeTree | 自动求和数值列 |
| 复杂聚合指标 | AggregatingMergeTree | 支持任意聚合函数 |
| 状态变更追踪 | CollapsingMergeTree | 符号位处理状态反转 |
| 实时写入缓冲 | Buffer | 内存缓冲后刷写 |
| 数据降采样 | GraphiteMergeTree | 时序数据自动聚合 |
| Kafka 消费 | Kafka | 消息队列直接入库 |
| 跨分片查询 | Distributed | 分布式代理层 |
| 外部数据查询 | MySQL/S3/URL/File | 联邦查询 |
6. 表引擎的关键配置
CREATE TABLE events (
event_time DateTime CODEC(Delta, LZ4),
-- CODEC 定义列的压缩算法
-- Delta: 差值编码,适合有序数据
-- LZ4: 快速压缩算法
-- ZSTD: 更高压缩率但需要更多 CPU
user_id UInt64 CODEC(T64, LZ4),
-- T64: 位压缩,适合整数
event_type LowCardinality(String),
-- LowCardinality: 字典编码,适合重复值多的列
description String CODEC(ZSTD(3))
-- ZSTD(3): 使用 ZSTD 压缩,级别为 3
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_time, user_id)
SETTINGS
index_granularity = 8192, -- 每多少行一个索引标记
merge_with_ttl_timeout = 86400, -- TTL 合并超时(秒)
storage_policy = 'hot_cold'; -- 存储策略(分层存储)
7. 总结
ClickHouse 的表引擎生态为不同场景提供了精确的工具:
- MergeTree 家族是核心:根据数据更新模式选择合适的变体
- 了解合并时序:所有 MergeTree 变体的特殊行为都发生在后台合并时
- 复制需要协调服务:Replicated 引擎需要 ZooKeeper 或 Keeper
- 集成引擎拓展边界:可以在不导入数据的情况下查询外部系统
引擎选择直接影响查询性能和数据一致性。在设计阶段投入时间选择正确的引擎,可以避免后期的重大重构。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。