数据库06:ClickHouse

从列式存储、MergeTree 引擎、分布式表到 OLAP 查询优化,理解 ClickHouse 在大数据分析场景下的极致性能设计。

字数 969 阅读时长 ≈ 3 分钟 2026-7-14 2026-7-14
数据库06:ClickHouse

ClickHouse 是俄罗斯 Yandex 公司开源的列式存储数据库,专为 OLAP(在线分析处理)场景设计。它的查询速度是传统关系型数据库的几十倍甚至上百倍。

这篇文章围绕四个核心点:列式存储、MergeTree 引擎、分布式表、OLAP 查询优化,理解 ClickHouse 的极致性能设计。

列式存储 vs 行式存储

传统数据库(MySQL、PostgreSQL)使用行式存储,而 ClickHouse 使用列式存储,这是它性能优势的根源。

存储方式对比

维度行式存储列式存储
数据组织按行存储,一行数据连续存放按列存储,一列数据连续存放
读取方式需要读取整行,即使只需要部分列只读取需要的列,减少 IO
压缩率低,数据类型多样高,同类数据压缩效果好
适用场景OLTP(在线事务处理)OLAP(在线分析处理)

列式存储的优势

  1. 减少 IO:分析查询通常只需要部分列,列式存储只读取需要的列
  2. 更高压缩率:同类数据更容易压缩,压缩率可达 10-20 倍
  3. 向量化执行:现代 CPU 支持 SIMD 指令,可以批量处理列数据
  4. 高效聚合:列数据连续存储,聚合操作效率高

MergeTree:ClickHouse 的核心引擎

MergeTree 是 ClickHouse 默认且最强大的存储引擎,它的设计思路是”写入时追加,后台合并”。

MergeTree 核心特性

  1. 主键索引:按主键排序,加速点查询和范围查询
  2. 分区:按分区键(如日期)组织数据,支持分区裁剪
  3. 数据合并:后台线程自动合并小数据块,优化查询性能
  4. 副本:支持副本机制,保证数据高可用
  5. TTL:支持数据自动过期

MergeTree 家族

引擎特点适用场景
MergeTree基础版本大多数场景
ReplacingMergeTree去重,保留最新版本需要去重的场景
SummingMergeTree预聚合,自动求和求和场景
AggregatingMergeTree预聚合,自定义聚合复杂聚合场景
CollapsingMergeTree折叠,处理增量数据日志分析
VersionedCollapsingMergeTree带版本的折叠需要保留历史版本

创建表示例

CREATE TABLE orders (
    id UInt64,
    user_id UInt64,
    amount Float64,
    status String,
    created_at DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 90 DAY;

分布式表:水平扩展

ClickHouse 通过分布式表实现水平扩展,将数据分散到多个节点。

分布式表架构

Distributed Table(分布式表)
  ├── Shard 1(分片1)
  │   └── Local Table(本地表)
  ├── Shard 2(分片2)
  │   └── Local Table(本地表)
  └── Shard 3(分片3)
      └── Local Table(本地表)

分片策略

策略方式优点缺点
哈希分片按字段哈希值分配数据均匀分布不支持范围查询优化
范围分片按字段范围分配范围查询高效需要手动维护分片规则
随机分片随机分配简单查询需要扫描所有分片

创建分布式表

-- 创建本地表
CREATE TABLE orders_local AS orders ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (user_id, created_at);

-- 创建分布式表
CREATE TABLE orders_all AS orders ENGINE = Distributed(
    cluster_name,
    database_name,
    orders_local,
    rand()  -- 分片键
);

查询路由

  • 本地查询:查询本地表,只访问当前节点
  • 分布式查询:查询分布式表,自动路由到所有分片
  • 数据写入:写入分布式表,自动分发到所有分片

OLAP 查询优化

ClickHouse 的查询优化和传统数据库有很大不同,需要针对 OLAP 场景进行调优。

分区裁剪

-- 好:只扫描 2024 年 1 月的分区
SELECT COUNT(*) FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2024-02-01';

-- 不好:扫描所有分区
SELECT COUNT(*) FROM orders WHERE toMonth(created_at) = 1;

主键索引

-- 好:使用主键索引
SELECT * FROM orders WHERE user_id = 123 AND created_at >= '2024-01-01';

-- 不好:无法使用主键索引
SELECT * FROM orders WHERE amount > 100;

预聚合

使用 SummingMergeTree 或 AggregatingMergeTree 进行预聚合:

CREATE TABLE orders_agg (
    user_id UInt64,
    month UInt64,
    total_amount AggregateFunction(sum, Float64),
    order_count AggregateFunction(count, UInt64)
) ENGINE = AggregatingMergeTree()
PARTITION BY month
ORDER BY user_id;

-- 写入聚合数据
INSERT INTO orders_agg SELECT 
    user_id,
    toYYYYMM(created_at) as month,
    sumState(amount),
    countState(*)
FROM orders
GROUP BY user_id, month;

-- 查询聚合数据
SELECT 
    user_id,
    sumMerge(total_amount),
    countMerge(order_count)
FROM orders_agg
GROUP BY user_id;

数据复制与高可用

ClickHouse 支持两种复制方式:

方式特点适用场景
ReplicatedMergeTree基于 ZooKeeper 的副本机制需要高可用的场景
外部复制使用外部工具(如 Kafka)同步数据数据管道场景

项目判断清单

  • 需要实时数据分析 → ClickHouse
  • 需要处理海量数据(TB/PB 级)→ ClickHouse
  • 需要复杂聚合查询 → ClickHouse
  • 需要高并发写入(日志、埋点)→ ClickHouse
  • 需要事务支持 → 不适合,考虑 PostgreSQL 或 TiDB
  • 需要实时查询最新数据 → 考虑 Kafka + ClickHouse
  • 需要多租户隔离 → 使用分布式表和用户权限