ClickHouse 列式存储深度解析:优点、缺点与选型实战

文章目录

    • [一、开篇:行存 vs 列存,一个例子看懂](#一、开篇:行存 vs 列存,一个例子看懂)
    • [二、ClickHouse 列式存储的六大优点](#二、ClickHouse 列式存储的六大优点)
      • [2.1 优点一:I/O 大幅降低](#2.1 优点一:I/O 大幅降低)
      • [2.2 优点二:高压缩比,存储成本低](#2.2 优点二:高压缩比,存储成本低)
      • [2.3 优点三:缓存命中率高](#2.3 优点三:缓存命中率高)
      • [2.4 优点四:压缩算法灵活](#2.4 优点四:压缩算法灵活)
      • [2.5 优点五:向量化执行](#2.5 优点五:向量化执行)
      • [2.6 优点六:COUNT(*) 优化](#2.6 优点六:COUNT(*) 优化)
    • [三、ClickHouse 列式存储的八大缺点](#三、ClickHouse 列式存储的八大缺点)
      • [3.1 缺点一:不支持事务(最致命)](#3.1 缺点一:不支持事务(最致命))
      • [3.2 缺点二:更新/删除成本极高](#3.2 缺点二:更新/删除成本极高)
      • [3.3 缺点三:无二级索引(非主键查询慢)](#3.3 缺点三:无二级索引(非主键查询慢))
      • [3.4 缺点四:点查性能弱,不适合高并发](#3.4 缺点四:点查性能弱,不适合高并发)
      • [3.5 缺点五:Join 实现特殊,大表 Join 风险高](#3.5 缺点五:Join 实现特殊,大表 Join 风险高)
      • [3.6 缺点六:窗口函数支持有限(已部分解决)](#3.6 缺点六:窗口函数支持有限(已部分解决))
      • [3.7 缺点七:SQL 方言差异](#3.7 缺点七:SQL 方言差异)
      • [3.8 缺点八:元数据管理需要人工干预](#3.8 缺点八:元数据管理需要人工干预)
    • 四、优缺点对照总表
    • [五、选型建议:什么时候选 ClickHouse?](#五、选型建议:什么时候选 ClickHouse?)
      • [5.1 强烈推荐使用 ClickHouse](#5.1 强烈推荐使用 ClickHouse)
      • [5.2 不建议使用 ClickHouse](#5.2 不建议使用 ClickHouse)
    • 六、总结

在数据库的世界里,行式存储与列式存储之争已持续多年。ClickHouse 凭借列式存储,在 OLAP 领域独树一帜。但列式存储并非银弹------它在带来极致查询性能的同时,也伴随着事务缺失、更新困难等代价。本文将从原理到实战,系统梳理 ClickHouse 列式存储的优缺点,并给出清晰的选型建议。


一、开篇:行存 vs 列存,一个例子看懂

假设有一张用户行为表,有 100 列,1 亿行。你需要统计今天每个页面的访问量(PV)

行式存储(如 MySQL)

  • 数据按行连续存放:[用户ID, 页面URL, 访问时间, 设备类型, IP, ...]
  • 执行 SELECT page_url, COUNT(*) ... 时,必须读取出所有 100 列的数据 ,然后从中取出 page_url
  • I/O 量 = 1 亿行 × 100 列 → 巨大

列式存储(如 ClickHouse)

  • 数据按列分开存放:page_url 列单独存一块,访问时间 列单独存一块
  • 执行上述查询时,只读取 page_url 这一列
  • I/O 量 = 1 亿行 × 1 列 → 极小

结论 :列式存储的核心优势就是 "读多少列,取多少列"


二、ClickHouse 列式存储的六大优点

2.1 优点一:I/O 大幅降低

场景 行存 I/O 列存 I/O 节省比例
100 列表,查 3 列 读 100 列 读 3 列 97%
查所有列 读 100 列 读 100 列 0%
COUNT(*) 扫某列索引或全表 读最小列的元数据 99%+

实际案例 :某日志表 50 列,查询只取 event_timeuser_idevent_type 3 列。行存每次扫描 50 列数据,列存只扫 3 列,I/O 减少 94%

2.2 优点二:高压缩比,存储成本低

同一列的数据类型相同,相邻值往往相似,压缩效果极佳。

列类型 典型压缩算法 压缩比 说明
时间戳 Delta + LZ4 10:1 ~ 50:1 前后差值极小
枚举/状态码 字典编码 100:1+ 实际只存整数代号
数值序列 Delta + ZSTD 5:1 ~ 20:1 递增序列压缩极高
随机 UUID 无法压缩 1:1 完全随机

实际案例 :某百亿级日志表,原始数据 100TB,ClickHouse 压缩后仅 12TB,压缩比超过 8:1。

2.3 优点三:缓存命中率高

压缩后的数据量小 → 同样大小的内存能缓存更多数据 → 重复查询命中内存,不落盘。

数据

  • 行存 :32GB 内存可缓存 32GB 原始数据(约 100%,因为行存基本不压缩)
  • 列存 :32GB 内存可缓存压缩后数据,相当于 160GB~320GB 原始数据(压缩比 5:1~10:1)

2.4 优点四:压缩算法灵活

ClickHouse 允许为不同列选择不同的压缩算法:

sql 复制代码
CREATE TABLE metrics (
    event_time DateTime CODEC(Delta, ZSTD),
    user_id UInt64 CODEC(ZSTD),
    event_type LowCardinality(String) CODEC(ZSTD),
    trace_id FixedString(32) CODEC(LZ4)
) ENGINE = MergeTree() ...
列类型 推荐算法 原因
时间序列 Delta + ZSTD 差值极小,压缩率极高
低基数字符串 ZSTD 字典自动压缩
随机值 LZ4 速度快,压缩率有限
数值列 T64 适合小整数范围

2.5 优点五:向量化执行

CPU 的 SIMD(单指令多数据流)指令可以一次性处理多个数据点。ClickHouse 的列式存储天然适配向量化:

cpp 复制代码
// 伪代码:计算 sum(price)
// 行存:逐行累加(需要不断跳转)
for row in rows:
    sum += row.price

// 列存:批量加载 256 个 price 到 CPU 寄存器,一次性累加
for i in 0..len(price_column)/256:
    vector = load_256(price_column + i*256)
    sum += sum_vector(vector)

效果 :列存下,SUM、AVG 等聚合函数的速度可比行存快 10-100 倍

2.6 优点六:COUNT(*) 优化

ClickHouse 对于 COUNT(*) 可以只读最小列(如分区键)的元数据,完全不扫数据:

sql 复制代码
-- 秒级返回,哪怕表有万亿行
SELECT COUNT(*) FROM giant_table;

三、ClickHouse 列式存储的八大缺点

3.1 缺点一:不支持事务(最致命)

ClickHouse 没有 ACID 事务,无法保证跨行、跨表的原子性操作。

不适用的场景

  • ❌ 银行转账(扣 A 账户 + 加 B 账户)
  • ❌ 订单扣库存(防止超卖)
  • ❌ 多表关联更新

替代方案:使用 PostgreSQL / MySQL 做事务主库,ClickHouse 做分析从库(通过 CDC 同步)。

3.2 缺点二:更新/删除成本极高

ClickHouse 不支持直接 UPDATE / DELETE,只能通过异步重写数据块实现:

sql 复制代码
-- 这条 SQL 会重写所有包含 user_id=123 的数据块
ALTER TABLE table UPDATE status = 'inactive' WHERE user_id = 123;

影响

  • 大表更新可能耗时 数十秒甚至数分钟
  • 不适合频繁更新场景

解决方案

  • 改用 追加 + 版本号 模式(查询时取最新版本)
  • 使用 ReplacingMergeTree 自动去重
  • 定期批量 ETL 而非实时更新

3.3 缺点三:无二级索引(非主键查询慢)

ClickHouse 只有稀疏主键索引(每 8192 行一个索引行),没有 B-Tree 或哈希索引。

查询类型 是否高效 原因
WHERE user_id = 123(user_id 是主键前缀) ✅ 高效 主键二分查找
WHERE status = 'active'(status 不是主键) ❌ 低效 全表扫描
WHERE page_url LIKE '%abc%' ❌ 极低效 无倒排索引

解决方案

  • 将高频过滤列加入 ORDER BY(主键)
  • 使用跳数索引(minmax、bloom_filter)作为有限补偿
  • 对低基数列,考虑使用 LowCardinality 类型

3.4 缺点四:点查性能弱,不适合高并发

ClickHouse 的稀疏索引和列式存储设计,使其不适合 QPS > 5000 的点查场景

场景 ClickHouse 推荐方案
SELECT * FROM table WHERE id = 123(QPS 5000+) ⚠️ 勉强可用,但不如 MySQL Redis / MySQL
仪表盘实时刷新(每秒数十次) ✅ 适合 ClickHouse 可承载
用户请求级查询(每次请求都查) ❌ 不适合 加缓存层(Redis)

3.5 缺点五:Join 实现特殊,大表 Join 风险高

ClickHouse 默认使用 Hash Join:将右表完全加载到内存中,再扫描左表。

风险

  • 右表过大(> 内存限制) → OOM
  • 多张大表 Join → 性能极差

解决方案

  • 右表使用 LowCardinality 降低内存
  • 将右表预先转化为字典(dictGet
  • 改用宽表代替 Join(数据冗余,查询快)
  • 使用 GLOBAL JOIN 配合分布式表

3.6 缺点六:窗口函数支持有限(已部分解决)

历史问题 :ClickHouse 早期版本不支持窗口函数。

现状:21.x 版本后已支持常用窗口函数。

sql 复制代码
-- 支持的窗口函数
row_number() OVER (PARTITION BY user_id ORDER BY event_time)
rank() OVER (...)
lag() / lead() OVER (...)
sum() OVER (...)

仍有局限

  • 性能不如专用数仓(如 Snowflake)
  • 某些复杂窗口计算仍需改写

3.7 缺点七:SQL 方言差异

ClickHouse 的 SQL 是标准 SQL 的超集,但也存在一些差异:

功能 标准 SQL ClickHouse 说明
LIKE 性能 一般 避免在大数据量下使用 %xxx%
IN 子查询 支持 支持,但需 GLOBAL IN 分布式场景需注意
JOIN 多个大表 极慢 避免,改用宽表
UPDATE / DELETE 行级 块级重写 完全不同

3.8 缺点八:元数据管理需要人工干预

某些场景下,ClickHouse 不会自动清理废弃数据:

  • 删除表后,/var/lib/clickhouse/data/ 目录可能残留
  • DETACHED 分区不会自动清理
  • 副本失败后的残留文件需手动清理

建议 :建立监控脚本,定期清理 system.detached_parts


四、优缺点对照总表

维度 优点 缺点
I/O 效率 只读需要的列,I/O 节省 90%+ 点查时需拼装多列,反而慢
压缩存储 压缩比 5~10 倍,成本低 写入时需解压/重压,CPU 开销大
查询性能 聚合查询快 10~100 倍 点查性能差,QPS 受限
事务 --- 不支持 ACID 事务
更新/删除 --- 成本极高,需重写数据块
索引 稀疏索引空间小 无二级索引,非主键过滤慢
Join --- 大表 Join 内存风险高
窗口函数 已支持常用函数 性能不如专用数仓
SQL 标准 标准 SQL 超集 存在方言差异(LIKE 等)
运维 --- 元数据管理需人工干预

五、选型建议:什么时候选 ClickHouse?

5.1 强烈推荐使用 ClickHouse

特征 说明
海量数据聚合分析 每天 TB~PB 级数据写入
宽表 + 少量列查询 表有 50+ 列,查询只取 3-5 列
时间范围过滤 查询总是带时间条件(分区裁剪)
高吞吐写入 每秒数十万行批量写入
弱事务/弱更新 数据几乎只追加,不修改
团队熟悉 SQL 不需要额外培训

5.2 不建议使用 ClickHouse

特征 替代方案
❌ 需要 ACID 事务 PostgreSQL / MySQL
❌ 频繁单行更新/删除 改用追加 + 版本号,或换 MySQL
❌ 高并发点查(QPS > 5000) Redis / MySQL
❌ 强一致性要求 需评估异步副本延迟
❌ 复杂多表 JOIN(多张大表) 改用宽表或换 Trino/Presto
❌ 全文搜索 Elasticsearch

六、总结

问题 答案
列式存储最大的好处是什么? I/O 减少 + 压缩率高 → 查询快、存储省
ClickHouse 最大的短板是什么? 不支持事务、更新/删除成本高
什么时候绝对不要用 ClickHouse? 金融交易、库存扣减、需要高并发点查
窗口函数能用吗? ✅ 21.x 后已支持
二级索引有吗? ❌ 没有,只能用主键 + 跳数索引替代

一句话记住

ClickHouse 列式存储:查询有多快,更新就有多难;压缩有多省,事务就有多弱。它是 OLAP 的利器,但别当 OLTP 来用。


如需深入了解 ClickHouse 的部署架构选型、分片与副本机制详解、分布式表原理剖析、无中心架构设计哲学、生产环境集群调优、多副本一致性实践、ClickHouse Keeper 核心原理等内容,请持续关注本专栏《ClickHouse 一站式从入门到实战》系列文章。

相关推荐
李白客1 小时前
MySQL迁移操作手册:一次完整迁移的实战路径
数据库·mysql
晴天¥2 小时前
Oracle 19c RAC修改监听默认端口
数据库·oracle
皮卡祺q2 小时前
【redis1】基本指令,五大数据类型,存储优化,使用场景】
数据库·redis·缓存
杜子不疼.2 小时前
Agent Skills 的演进治理与 Swarm Skills 自演进
服务器·数据库·microsoft
wanghowie2 小时前
26.v3 核心升级:语义层 + 指标体系——禁止 LLM 直连 SQL
数据库·sql
袋鼠云数栈2 小时前
数栈 V7.0 多模态数据智能平台:打造 AI-Ready 的企业数据底座
大数据·数据结构·数据库·人工智能·数据治理·多模态
Mr. zhihao2 小时前
Redis Bitmap:BitCount、bitTop的使用业务场景
数据库·redis·缓存
永远不会出bug2 小时前
PgSql数据库函数
数据库
Volunteer Technology2 小时前
Flink Sink
大数据·数据库·flink