ClickHouse + DolphinScheduler:两个组件搞定轻量离线数仓,谁还堆 Hadoop 全家桶?
搭数仓一定要上 Hadoop 全家桶吗?本文手把手带你用 ClickHouse + DolphinScheduler 搭建一套轻量离线数仓,从建库到调度一条龙,中小团队也能拥有自己的数据底座。
前言:你是不是也被"全家桶"劝退过?
提到离线数仓,很多人脑子里自动弹出一套豪华套餐:
Hadoop + Hive + Spark + DolphinScheduler + Sqoop/DataX + 可能还要再来个 OLAP 引擎……
光是把这些组件部署完,运维同学的头发已经掉了一半,项目还没正式开始。
但现实是:不是每个团队都需要日均 PB 级的处理能力,也不是每个数仓都要搞得像 NASA 指挥中心一样复杂。对于日增数据量在千万级以下的中小团队来说,有一条"轻装上阵"的路线 —— ClickHouse + DolphinScheduler,两个组件,就能撑起一套五脏俱全的离线数仓。
两条路线对比
| 维度 | 全家桶方案 | 轻量方案 |
|---|---|---|
| 技术栈 | Hadoop + Hive + Spark + 调度 + ETL + OLAP | ClickHouse + DolphinScheduler |
| 部署复杂度 | ⭐⭐⭐⭐⭐(建议先买防脱洗发水) | ⭐⭐(Docker 一把梭) |
| 运维成本 | 高(N 个组件 = N 种报错姿势) | 低(两个组件,心里有数) |
| 适用场景 | 大规模离线计算、数据湖 | 中小规模数仓、快速交付的 BI 场景 |
| 查询性能 | 依赖引擎组合 | ClickHouse 原生 OLAP,天然快 |
不是说全家桶不好,而是杀鸡别用牛刀 —— 如果你的数据量还没大到需要 Hadoop 来扛,先试试轻量方案,省下来的时间可以多写几个需求(好吧,这可能不算好处 😅)。
一、前置准备
在开始之前,你需要先把两个核心组件部署好:
| 组件 | 参考文档 |
|---|---|
| ClickHouse | ClickHouse 25.4 基于 Docker 单机部署实战指南 |
| DolphinScheduler | DolphinScheduler 单机部署实战:Docker 一把梭,中小团队的调度神器 |
部署完这两个,我们就可以正式"搬砖"了。
二、数仓分层:先把"房间"隔好
数仓的经典分层模型,即使是轻量方案也不能含糊。在 ClickHouse 中,我们直接用 Database 来划分各层:
CREATE DATABASE IF NOT EXISTS ods; -- 原始数据层(贴源层)
CREATE DATABASE IF NOT EXISTS dwd; -- 明细数据层
CREATE DATABASE IF NOT EXISTS dws; -- 汇总数据层
CREATE DATABASE IF NOT EXISTS dim; -- 维度层
CREATE DATABASE IF NOT EXISTS ads; -- 应用数据层(直接给 BI 用)
五句 SQL,数仓的"骨架"就搭好了。是的,就这么朴实无华。
💡 小贴士:分层不是为了好看,而是为了让数据流转有据可循 —— ODS 管"搬进来",DWD 管"洗干净",DWS 管"聚合好",ADS 管"端上桌"。每一层各司其职,后续排查问题的时候你会感谢自己。
三、数据同步:把数据"搬"进 ClickHouse
3.1 同步方案选型
数据从 MySQL、MongoDB 等业务库同步到 ClickHouse,常见方案分两大类:
| 方案 | 思路 | 代表工具 | 特点 |
|---|---|---|---|
| ETL/ELT 中间件 | 部署独立的同步工具 | DataX、Sqoop、Flink CDC | 功能强大,但多一个组件多一份运维 |
| CK 原生表引擎 | ClickHouse 直接连数据源 | MySQL 引擎、MongoDB 引擎、JDBC 引擎等 | 零额外部署,SQL 即同步 |
ClickHouse 内置了丰富的集成表引擎,支持 MySQL、MongoDB、PostgreSQL、Kafka、S3、HDFS 等几十种数据源,基本覆盖了常见的数据接入场景。
我们的选择:既然目标是"轻量",那就贯彻到底 —— 直接用 ClickHouse 原生 MySQL 表引擎,不引入额外组件,SQL 写完数据就过来了。
🎯 原则:能少一个组件就少一个,少一个组件就少一种凌晨三点被叫醒的可能。
3.2 创建数据源映射(MySQL 表引擎)
在 ClickHouse 中,通过 MySQL 表引擎直接"挂载"远端 MySQL 表:
CREATE TABLE source_mapping.member_profile
ENGINE = MySQL(
'your-mysql-host:3306',
'your_database',
'member_profile',
'your_username',
'your_password'
);
⚠️ 注意:这里只是创建了一个"映射",数据仍然存储在 MySQL 中。每次查询这张表,ClickHouse 都会实时去 MySQL 拉数据。所以它的定位是同步的"桥梁",而不是最终存储。
3.3 创建 ODS 层目标表
接下来,在 ODS 层创建真正存储数据的本地表:
CREATE TABLE ods.ods_member_profile (
id UInt64 COMMENT '主键ID',
member_id String COMMENT '会员编号',
avatar_url Nullable(String) COMMENT '头像地址',
login_key String COMMENT '登录唯一标识',
phone Nullable(String) COMMENT '手机号码',
member_type Int8 DEFAULT 1 COMMENT '会员类型: 1-普通, 2-内部',
status Nullable(Int8) COMMENT '状态: 0-禁用, 1-启用',
secret_key Nullable(String) COMMENT '密钥(已加密)',
nickname Nullable(String) COMMENT '昵称',
avatar_large Nullable(String) COMMENT '大头像地址',
_version DateTime DEFAULT now() COMMENT '版本字段,用于数据更新'
)
ENGINE = ReplacingMergeTree(_version)
PARTITION BY toYYYYMM(create_time)
ORDER BY (member_id)
SETTINGS
index_granularity = 256
COMMENT 'ODS层 - 会员信息表';
几个关键设计决策解释一下:
ReplacingMergeTree:这是重点!ODS 层会反复同步增量数据,同一条记录可能被多次写入。ReplacingMergeTree会在后台 Merge 时自动按_version去重,保留最新版本。如果用普通MergeTree,你的数据会越来越"膨胀",最终查出来一堆重复行,场面一度非常混乱 🤯PARTITION BY toYYYYMM(create_time):按月分区,方便管理和清理历史数据ORDER BY (member_id):按业务主键排序,查询效率拉满
3.4 初始化存量数据
首次同步,需要把 MySQL 中的全量数据灌进来:
INSERT INTO ods.ods_member_profile (
id, member_id, avatar_url, login_key, phone,
member_type, status, secret_key, nickname, avatar_large
)
SELECT
id,
member_id,
ifNull(avatar_url, '') AS avatar_url,
login_key,
ifNull(phone, '') AS phone,
member_type,
ifNull(status, 0) AS status,
ifNull(secret_key, '') AS secret_key,
ifNull(nickname, '') AS nickname,
ifNull(avatar_large, '') AS avatar_large
FROM source_mapping.member_profile;
执行完毕后,验证一下数据量:
SELECT count(*) FROM ods.ods_member_profile FINAL;
💡 为什么用
FINAL? 因为ReplacingMergeTree的去重发生在后台 Merge 过程中,如果 Merge 还没完成,直接count(*)可能会看到重复数据。加上FINAL关键字,ClickHouse 会在查询时强制去重,得到准确结果。(当然,FINAL有性能开销,生产环境大表慎用,验证的时候用用无妨。)
顺便说一下同步性能 —— 你可能会担心"全量灌数据会不会跑到天荒地老?"实测下来,完全不用担心:
| 数据源 | 同步速率 | 每分钟吞吐量 |
|---|---|---|
| MySQL → ClickHouse | ~35w 条/s | 2000w+ 条/min |
| MongoDB → ClickHouse | ~12w 条/s | 720w+ 条/min |
以上数据基于多张业务表的实际同步统计,具体性能受网络、字段数量、数据类型等因素影响,仅供参考。但总的来说,ClickHouse 的写入能力可以用"暴力"来形容 —— 千万级的表,几分钟就灌完了,泡杯咖啡的功夫都不用 ☕
四、增量调度:让 DolphinScheduler 帮你"打工"
存量数据搞定了,但数仓是要"活"的 —— 业务库每天都有新数据进来,我们需要定时增量同步。这时候就轮到 DolphinScheduler 登场了。
4.1 创建调度工作流
操作路径很简单:创建项目 → 新建工作流 → 添加 SQL 节点 → 上线定时,DolphinScheduler 的 UI 交互比较直观,这里不赘述基础操作(如有不熟悉的同学可留言评论,后续补充分享相关内容的文章)。
我们重点关注数仓场景下常用的三种节点类型:
| 模块 | 组件 | 用途 |
|---|---|---|
| 通用组件 | SQL | 执行 MySQL / ClickHouse SQL,数仓的主力 |
| 通用组件 | SHELL | 发通知(飞书/钉钉)、执行脚本等辅助操作 |
| 逻辑节点 | DEPENDENT | 依赖上游工作流,控制层级间的执行顺序 |
4.2 增量同步 SQL(核心)
在工作流中添加一个 SQL 节点,填写增量同步逻辑 —— 这才是本章的重头戏:
INSERT INTO ods.ods_member_profile (
id, member_id, avatar_url, login_key, phone,
member_type, status, secret_key, nickname, avatar_large
)
SELECT
id,
member_id,
ifNull(avatar_url, '') AS avatar_url,
login_key,
ifNull(phone, '') AS phone,
member_type,
ifNull(status, 0) AS status,
ifNull(secret_key, '') AS secret_key,
ifNull(nickname, '') AS nickname,
ifNull(avatar_large, '') AS avatar_large
FROM source_mapping.member_profile
WHERE update_time >= toDateTime('$[yyyy-MM-dd HH:00:00-1/24]')
AND create_time < toDateTime('$[yyyy-MM-dd HH:00:00]');
📌 增量逻辑说明:
$[yyyy-MM-dd HH:00:00-1/24]是 DolphinScheduler 的内置时间参数,表示上一个整点$[yyyy-MM-dd HH:00:00]表示当前整点- 每次只同步过去一小时内有更新的数据,配合
ReplacingMergeTree的去重机制,实现"幂等增量同步"🔍 WHERE 条件拆解:
update_time >= 开始时间:捞出该时段内新增或修改过的记录(新增时 update_time = create_time,修改时 update_time 会被刷新)create_time < 结束时间:排除掉在当前整点之后才创建的数据,避免"抢跑"- 两个条件取交集,精准圈定"本小时内发生变更的数据"
⚡ 性能提示:MySQL 源表务必创建
(update_time, create_time)联合索引,否则每小时一次的增量查询会变成全表扫描,慢查询警告了解一下 🚨
4.3 上线定时调度
保存工作流后,上线并配置定时策略(比如每小时执行一次):

💡 调度时间小技巧:建议将执行时间比整点延后 3~5 分钟(比如每小时的第 5 分钟触发)。业务库通常有 MySQL 主从复制延迟,卡在整点跑可能漏掉最后几秒写入的数据。稍微"慢半拍",数据反而更完整。
4.4 验证同步结果
调度跑完后,查一下最新数据是否已经同步过来:
SELECT *
FROM ods.ods_member_profile FINAL
ORDER BY create_time DESC
LIMIT 10;
看到最新的业务数据已经出现在 ODS 层,说明整个链路已经打通 🎉
五、后续建设:从 ODS 到 ADS 的数据流转
ODS 层只是起点,完整的数仓还需要继续向上构建
每一层之间的 ETL 逻辑,同样通过 DolphinScheduler 的 SQL 任务节点来调度,利用 DEPENDENT 节点控制层级间的依赖关系,确保上游跑完下游才启动。

至此,一套轻量但完整的离线数仓就搭建完成了。
六、总结
回顾一下我们做了什么:
| 步骤 | 内容 | 关键技术点 |
|---|---|---|
| 1 | 部署 ClickHouse + DolphinScheduler | Docker 部署,开箱即用 |
| 2 | 创建数仓分层(ODS/DWD/DWS/DIM/ADS) | ClickHouse Database 划分 |
| 3 | 配置数据源映射 | MySQL 表引擎,零组件接入 |
| 4 | 全量初始化 + 增量调度 | ReplacingMergeTree + 时间窗口 |
| 5 | 逐层构建数仓 | DolphinScheduler 工作流编排 |
这套方案的核心优势:
- 极简部署:只有两个组件,Docker 一把梭,半天搞定
- 零额外 ETL 组件:利用 ClickHouse 原生表引擎直连数据源,不用再折腾 DataX、Sqoop
- 天然 OLAP 能力:ClickHouse 本身就是 OLAP 引擎,数仓查询不需要再套一层
- 调度灵活:DolphinScheduler 支持 DAG 编排、依赖管理、失败重试、告警通知,麻雀虽小五脏俱全
当然,这套方案也有其适用边界 —— 如果你的数据量级已经到了 TB/PB 级别,或者需要复杂的流批一体处理,那还是老老实实上大数据全家桶吧。工具没有高低之分,只有合不合适。
📣 如果这篇文章帮你少踩了一个坑,或者让你对轻量数仓有了新的思路,欢迎 点赞 👍 收藏 ⭐ 关注,你的支持是我持续输出的动力!有任何问题也欢迎评论区交流,我们一起把数仓这件事搞得明明白白 💪
更多推荐



所有评论(0)