ClickHouse是面向OLAP场景的列式数据库,在亿级数据量的聚合查询上性能远超传统行式数据库。日志分析、用户行为统计、实时大屏指标计算是ClickHouse的典型应用场景。本文涵盖MergeTree引擎配置、分布式集群搭建、物化视图预聚合和数据写入策略的完整方案。
ClickHouse列式存储引擎与MergeTree家族
ClickHouse存储数据按列组织,查询时只读取所需列,大幅减少IO。MergeTree是ClickHouse最核心的表引擎,支持主键索引、数据分区和数据TTL。MergeTree家族包含多个变体:
- MergeTree:基础引擎,按主键排序存储,支持分区和稀疏索引
- ReplacingMergeTree:相同主键保留最新版本,实现去重(合并时触发,非实时)
- SummingMergeTree:相同主键的数值列自动求和,适合预聚合场景
- AggregatingMergeTree:配合物化视图使用,存储聚合中间状态
- CollapsingMergeTree:通过sign字段控制插入和删除,适合频繁更新场景
-- 创建MergeTree表(用户行为日志)
CREATE TABLE events_raw
(
event_date Date,
event_time DateTime,
user_id UInt64,
event_type String,
page_id String,
duration_ms UInt32,
country LowCardinality(String),
device LowCardinality(String)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id, event_time)
SETTINGS index_granularity = 8192;
-- LowCardinality(String)对低基数列使用字典编码,大幅降低存储和查询开销
-- PARTITION BY按月分区,利于按时间范围查询和清理历史数据
-- ORDER BY定义排序键和主键索引,将最常用的过滤字段放在前面
-- 创建ReplacingMergeTree表(用户最新状态)
CREATE TABLE user_profile
(
user_id UInt64,
nickname String,
level UInt16,
last_login DateTime,
update_time DateTime
)
ENGINE = ReplacingMergeTree(update_time)
PARTITION BY toYYYYMM(last_login)
ORDER BY user_id;
-- ReplacingMergeTree不会实时去重,查询时需要使用FINAL关键字或去重查询
分布式集群搭建与Distributed表配置
ClickHouse分布式架构由Shard(分片)和Replica(副本)组成。每个Shard包含一个或多个Replica实现高可用。Distributed表本身不存储数据,而是作为路由层将查询分发到各Shard的本地表。
-- 在每个节点上创建本地表(ReplicatedMergeTree自动同步副本数据)
CREATE TABLE events_raw_local ON CLUSTER analytics_cluster
(
event_date Date,
event_time DateTime,
user_id UInt64,
event_type String,
page_id String,
duration_ms UInt32,
country LowCardinality(String),
device LowCardinality(String)
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/events_raw_local',
'{replica}'
)
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id, event_time);
-- 创建Distributed表
CREATE TABLE events_raw ON CLUSTER analytics_cluster
AS events_raw_local
ENGINE = Distributed(
analytics_cluster,
default,
events_raw_local,
rand()
);
-- Distributed表不存储数据,INSERT时按分片键路由到各节点
-- SELECT时自动将查询分发到所有Shard并合并结果
分片键选择策略:如果查询经常按user_id过滤,使用cityHash64(user_id)分片使同一用户数据落在同一Shard,避免跨节点Join。如果查询均匀分布在所有数据上,rand()随机分片更均衡。internal_replication设为true后,写入只需发到一个Replica,ReplicatedMergeTree通过ZooKeeper自动同步到其他副本。
物化视图预聚合与查询加速
ClickHouse的物化视图在数据写入时自动触发,将原始数据按预定义规则聚合后写入目标表。查询直接读取预聚合表,跳过原始数据扫描,性能提升可达数十倍。
-- 创建AggregatingMergeTree预聚合表
CREATE TABLE events_daily_agg
(
event_date Date,
event_type String,
country LowCardinality(String),
unique_users AggregateFunction(uniq, UInt64),
total_events UInt64,
avg_duration SimpleAggregateFunction(avg, UInt32),
max_duration SimpleAggregateFunction(max, UInt32)
)
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, event_type, country);
-- 创建物化视图(写入触发自动聚合)
CREATE MATERIALIZED VIEW events_daily_mv
TO events_daily_agg
AS
SELECT
event_date,
event_type,
country,
uniqState(user_id) AS unique_users,
count() AS total_events,
avgState(duration_ms) AS avg_duration,
maxState(duration_ms) AS max_duration
FROM events_raw
GROUP BY event_date, event_type, country;
-- 查询预聚合表(比查原始表快10-50倍)
SELECT
event_date,
event_type,
uniqMerge(unique_users) AS uv,
total_events,
avgMerge(avg_duration) AS avg_duration,
maxMerge(max_duration) AS max_duration
FROM events_daily_agg
WHERE event_date BETWEEN '2026-09-01' AND '2026-09-18'
AND country = 'China'
GROUP BY event_date, event_type
ORDER BY event_date DESC;
uniqState存储uniq聚合的中间状态,uniqMerge合并中间状态得到最终结果。这种设计允许增量聚合——多次写入的数据各自聚合,查询时合并所有中间状态,结果与全量聚合一致。SimpleAggregateFunction用于简单可合并的聚合函数(sum、max、min),无需State/Merge操作,存储和查询更高效。
数据批量写入与副本同步策略
ClickHouse对写入模式有严格要求:高频小批量写入会导致大量part未合并,影响查询性能甚至触发”Too many parts”异常。
-- 推荐写入方式:批量插入(每批次1万-10万行)
INSERT INTO events_raw
SELECT
toDate(event_time) AS event_date,
event_time,
user_id,
event_type,
page_id,
duration_ms,
country,
device
FROM input_table;
ClickHouse客户端写入建议使用Buffer表或在应用层攒批。Buffer表将写入暂存在内存Buffer中,达到阈值后自动刷入底层MergeTree。
-- Buffer表配置
CREATE TABLE events_buffer AS events_raw
ENGINE = Buffer(default, events_raw,
16, 100, 10000, 100000, 10000000, 100000000
);
-- 写入buffer表,自动批量刷入events_raw
INSERT INTO events_buffer VALUES (...);
生产环境推荐使用Kafka + ClickHouse Kafka Engine方案,Kafka消费积攒后批量写入ClickHouse。定期清理历史数据通过分区DROP实现,DROP PARTITION是原子操作且立即释放磁盘空间,比DELETE高效。TTL表达式可自动过期分区数据:ALTER TABLE events_raw MODIFY TTL event_date + INTERVAL 90 DAY,90天前的分区自动删除。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/clickhouse-lie-shi-cun-chu-shi-zhan-fen-bu-shi-biao-chuang/