一个被大厂面试官反复追问的问题: “你们项目里为什么用 ClickHouse 存中间结果,而不用 MySQL?”
今天,我们就来详细剖析下。
这是一个“数据属性决定技术选型”的问题。
什么是“中间结果数据”?先统一概念
在很多实际项目中(风控、推荐、搜索、数据任务系统),都会存在这样一类数据:
- 上游任务计算产生
- 下游多个模块消费
- 本身不是最终业务结果
我们通常称之为 中间结果数据。
1. 中间结果的典型来源
- 离线 / 准实时任务计算结果
- 多阶段任务 pipeline 的中间产物
- 策略命中明细、召回明细、评分结果
- 行为日志的预聚合宽表
2. 中间结果的典型特征
这是整个选型的核心前提:
-
数据量大:百万、千万,甚至亿级
-
生命周期有限(如保留 7~30 天)【业务场景无需长期留存。MySQL 能实现 “分区过期自动清理”,但无原生 TTL 语法,需通过「分区策略 + 定时任务」组合完成;】
-
写后很少更新(或完全不更新)
-
写入方式明确:批量写入
-
查询以分析为主:
- 扫描
group by- 聚合统计(count / sum / uniq)
- 排序、TopN、多维分析
⚠️ 注意: 它不是“查一条、改一条”的数据,而是“扫一片、算一堆”的数据。
从查询模式出发
中间结果的真实查询模式
在实际使用中,这类数据的查询往往是:
- 大范围 scan
- 多字段 group by
- 多维聚合
- 面向下游任务或 BI 看板
而几乎不会:
where id = ?- 单行 update
- 事务性修改
| 维度 | 中间结果数据 | OLTP | OLAP |
|---|---|---|---|
| 数据量 | 大 | 小 | 大 |
| 查询模式 | 聚合、统计 | 点查 | 批量分析 |
| 更新频率 | 低 | 高 | 低 |
| 是否需要事务 | 否 | 是 | 否 |
为什么 MySQL 不适合存中间结果?
这里不是说 MySQL 不行,而是 它解决的是另一个问题。
1. 行存模型决定了扫描成本
MySQL(InnoDB)是典型的 行存数据库:
- 每一行的数据是一起存储的
- 即使只用到 2 个字段,也需要读取整行
在中间结果这种:
- 大量 group by
- 多维聚合
的场景下,会带来:
- 大量无效 IO
- CPU cache 命中率低
- 查询耗时随数据量快速上升
2. 索引在这个场景下很难设计
中间结果数据的维度往往很多:
- 任务 ID
- 业务 ID
- 业务维度
如果用 MySQL:
-
建索引:
- 组合索引数量爆炸
-
不建索引:
- 查询只能全表 scan
最终结果往往是:
索引建了很多,但真正能命中的很少。
3. MySQL 的“优势”在这里变成了成本
MySQL 的核心价值在于:
- 事务(ACID)
- 行级锁
- 强一致性
但在中间结果场景中:
- 不需要事务
- 不需要回滚
- 不存在并发更新
这些能力:
不是优势,而是额外的系统负担。
4. 扩展与成本问题
当中间结果数据规模上来后:
-
MySQL 往往需要:
- 分库分表
- 多实例
-
运维复杂度明显提升
而这些成本,在 ClickHouse 中往往可以通过:
- 列式存储
- 高压缩比
- 单机高吞吐
来规避。
为什么 ClickHouse 正好匹配中间结果?
ClickHouse 的设计目标就是:
用最小的资源,完成最大规模的数据分析。
1. 列式存储 + 向量化执行
- 只读取查询涉及的列
- 批量计算,CPU 利用率高
- group by / 聚合性能极强
2. 顺序 IO + 高压缩
- 数据按列压缩,压缩比高
- 顺序扫描磁盘,吞吐稳定
- 非常适合中间宽表
3. 写入模型与中间结果天然一致
- 批量写入
- 写完即查
- 不鼓励 update,反而促使设计不可变数据模型
4. 成本与性能比极高
在很多实际项目中:
一台 ClickHouse 的分析能力,可以替代多台 MySQL 实例。
综上:中间结果数据本质是分析型数据,数据量大、更新少、查询以聚合为主。 MySQL 是 OLTP 行存数据库,在这个场景下扫描和扩展成本很高; ClickHouse 是列式 OLAP 引擎,与这种数据形态和查询模式天然匹配,因此 ClickHouse 是一个不错的选择。