为什么中间结果数据选择 ClickHouse 而不是 MySQL?

55 阅读4分钟

一个被大厂面试官反复追问的问题: “你们项目里为什么用 ClickHouse 存中间结果,而不用 MySQL?”

今天,我们就来详细剖析下。

这是一个“数据属性决定技术选型”的问题。

什么是“中间结果数据”?先统一概念

在很多实际项目中(风控、推荐、搜索、数据任务系统),都会存在这样一类数据:

  • 上游任务计算产生
  • 下游多个模块消费
  • 本身不是最终业务结果

我们通常称之为 中间结果数据

1. 中间结果的典型来源

  • 离线 / 准实时任务计算结果
  • 多阶段任务 pipeline 的中间产物
  • 策略命中明细、召回明细、评分结果
  • 行为日志的预聚合宽表

2. 中间结果的典型特征

这是整个选型的核心前提

  • 数据量大:百万、千万,甚至亿级

  • 生命周期有限(如保留 7~30 天)【业务场景无需长期留存。MySQL 能实现 “分区过期自动清理”,但无原生 TTL 语法,需通过「分区策略 + 定时任务」组合完成;】

  • 写后很少更新(或完全不更新)

  • 写入方式明确:批量写入

  • 查询以分析为主

    • 扫描
    • group by
    • 聚合统计(count / sum / uniq)
    • 排序、TopN、多维分析

⚠️ 注意: 它不是“查一条、改一条”的数据,而是“扫一片、算一堆”的数据。

从查询模式出发

中间结果的真实查询模式

在实际使用中,这类数据的查询往往是:

  • 大范围 scan
  • 多字段 group by
  • 多维聚合
  • 面向下游任务或 BI 看板

几乎不会

  • where id = ?
  • 单行 update
  • 事务性修改
维度中间结果数据OLTPOLAP
数据量
查询模式聚合、统计点查批量分析
更新频率
是否需要事务

为什么 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 是一个不错的选择。