【大数据技术实战】Flink+DS+Dinky 自动化构建数仓平台

一、背景:企业数仓建设的现状与挑战

在数字化转型进入深水区的今天,数据已成为企业核心生产要素,而实时数仓作为 "数据驱动决策" 的关键载体,其建设水平直接决定企业在市场竞争中的响应速度与决策精度。根据 IDC《2024 年全球大数据市场报告》,超过 78% 的企业将 "实时数据处理能力" 列为数字化转型的核心目标,但在实际落地中,传统数仓架构却面临一系列难以突破的瓶颈,这些瓶颈集中体现在 "开发、运维、架构" 三个维度,严重制约数仓价值的释放。

1.1 开发效率低下:技术门槛高,迭代周期长

Apache Flink 作为当前实时计算领域的主流引擎,其强大的流处理能力与状态管理机制已成为实时数仓的核心支撑。但 Flink 原生开发模式存在显著的技术门槛,导致开发效率低下:

  • 语言与 API 复杂度:原生 Flink 开发需掌握 Java/Scala 语言,且需熟悉 DataStream API、Table API 等复杂接口。以 "Kafka 数据清洗后写入 MySQL" 为例,传统开发需编写至少 200 行代码(含连接器配置、序列化 / 反序列化、状态管理),开发周期长达 2-3 天,且需专人维护代码版本;
  • 版本兼容难题:Flink 各版本(如 1.17、1.18、1.19)间存在 API 差异(如 Kafka 连接器参数变化、状态后端优化),企业若需升级 Flink 版本,需批量修改历史作业代码,适配成本极高;
  • 调试体验差:原生 Flink 调试依赖本地集群或远程提交,日志分散在 TaskManager 节点,排查一个 "数据解析失败" 问题需逐一查看节点日志,平均耗时 1-2 小时;
  • SQL 支持有限:Flink SQL Client 仅提供命令行交互,无语法提示、格式化、血缘分析功能,非开发人员(如数据分析师)无法直接参与实时作业开发,需依赖开发团队协作,进一步拉长迭代周期。

某电商企业案例显示,传统模式下,一个 "实时销量大屏" 需求从开发到上线需 10 天(含需求沟通、代码开发、调试、部署),而业务部门要求 "大促前 3 天紧急上线",导致需求无法按时交付,错失业务决策窗口。

1.2 运维管理碎片化:全链路协同难,故障排查效率低

实时数仓是 "数据采集→计算处理→存储落地→监控告警" 的全链路体系,但传统架构中,各环节工具脱节,形成 "数据孤岛" 与 "运维孤岛",具体痛点如下:

1.2.1 任务管理与调度分离
  • 传统架构中,Flink 作业开发依赖 IDE(如 IntelliJ IDEA),调度依赖 Cron 脚本或简单调度工具(如 Azkaban),两者无联动:
    • 若上游数据源(如 Kafka)延迟,Cron 脚本仍会按时触发 Flink 作业,导致作业读取空数据,需手动重启;
    • 多个 Flink 作业存在依赖关系(如 "用户行为计算作业→用户画像更新作业"),需手动维护依赖顺序,易出现 "上游未完成,下游已执行" 的问题;
  • 某金融企业曾因 "风控数据计算作业未完成,下游风险预警作业已执行",导致漏判高风险交易,产生百万级损失。
1.2.2 元数据无持久化管理
  • Flink 原生元数据(表结构、UDF、数据源配置)存储在内存中,会话结束或集群重启后,所有元数据丢失:
    • 运维人员需重新执行 DDL 语句创建表、注册 UDF,一个包含 10 张表的数仓,每次重启需耗时 1 小时;
    • 元数据无版本控制,若表结构变更(如新增字段),历史作业可能因 "字段不匹配" 失败,且无法追溯变更记录;
  • 某 IoT 企业因 Flink 集群重启,元数据丢失,导致 12 个设备监控作业中断,2 小时后才恢复,期间无法实时监测设备故障,造成生产停滞。
1.2.3 监控告警体系不完善
  • 传统架构中,Flink 作业监控依赖 Flink WebUI,调度任务监控依赖调度工具日志,元数据变更无监控,三者无统一入口:
    • 作业失败后,需分别查看 Flink 日志、调度工具日志、数据库日志,排查故障平均耗时 30 分钟;
    • 无 "全链路告警" 机制,若 Kafka 集群宕机,Flink 作业因 "无数据输入" 处于空闲状态,但无告警通知,运维人员无法及时发现;
  • 某零售企业曾因 Kafka 集群故障,Flink 实时库存计算作业停滞 4 小时,导致线上库存显示错误,用户下单后无法发货,引发大量投诉。

1.3 架构扩展性弱:批流分离,多场景适配难

传统数仓多采用 "Lambda 架构",即 "批处理层(Hive)+ 流处理层(Flink)+ 服务层(MySQL)",但该架构存在先天缺陷,无法满足企业多样化数据需求:

1.3.1 批流数据口径不一致
  • 批处理与流处理采用两套计算逻辑:
    • 批处理层用 Hive SQL 计算 "每日订单总额",流处理层用 Flink SQL 计算 "实时订单总额";
    • 因计算逻辑差异(如时间窗口定义、空值处理规则),导致批流结果偏差(如批处理结果 100 万,流处理结果 98 万),业务部门无法确定 "哪组数据是正确的";
  • 某电商企业大促后,批处理统计的 "大促总销售额" 为 5.2 亿,流处理实时统计为 4.9 亿,差异 3000 万,需投入 3 人天排查差异原因,影响财务报表生成。
1.3.2 多数据源适配成本高
  • 新增数据源(如 HBase、Elasticsearch、Pulsar)时,需开发自定义 Flink 连接器:
    • 开发一个 HBase 连接器需掌握 HBase API 与 Flink Connector 规范,周期约 3 天;
    • 连接器无统一管理,不同团队重复开发,导致技术债务累积;
  • 某互联网企业因业务需求,需接入 Pulsar 数据源,开发连接器耗时 5 天,期间无法推进实时推荐作业开发,影响新功能上线。
1.3.3 资源利用率低
  • 批处理作业(如每日凌晨执行的 Hive 全量计算)需占用大量资源,流处理作业需长期占用资源,两者无法共享资源:
    • 白天流处理作业资源空闲,但批处理作业未执行;凌晨批处理作业资源紧张,流处理作业资源无法临时调度给批处理;
    • 某金融企业为保障批处理作业执行,额外采购 5 台服务器,资源利用率仅 30%,造成硬件成本浪费。

二、问题解决:自动化数仓平台的核心目标与价值

针对上述痛点,本方案通过 "Flink+DolphinScheduler+Dinky" 组合,构建一站式自动化实时数仓平台,以 "降本、提效、稳定、扩展" 为核心目标,具体如下:

2.1 降本:降低技术门槛,减少资源浪费

  • 开发门槛降低:通过 Dinky 的 FlinkSQL Studio,将 Flink 作业开发从 "代码级" 简化为 "SQL 级",非开发人员(如数据分析师)也可参与开发,减少对专业 Flink 开发人员的依赖;
  • 资源利用率提升:基于 Flink 批流一体能力,批处理与流处理共享集群资源,白天流处理作业空闲时,资源可调度给临时批处理任务,资源利用率从 30% 提升至 70%;
  • 运维成本减少:自动化元数据管理、统一监控告警,减少 80% 手动运维操作(如重复建表、日志排查),一个运维人员可管理 100+ Flink 作业,较传统模式效率提升 5 倍。

2.2 提效:全链路自动化,缩短迭代周期

  • 开发效率提升:Dinky 提供 SQL 语法提示、一键调试、血缘分析功能,一个 "Kafka→MySQL" 的 Flink 作业开发周期从 2-3 天缩短至 1 小时,整体开发效率提升 300%;
  • 调度效率提升:DolphinScheduler 可视化工作流编排,支持 "数据就绪触发""作业依赖联动",无需手动维护 Cron 脚本,调度配置时间从 1 天缩短至 10 分钟;
  • 故障排查效率提升:统一监控平台整合 Flink 作业日志、调度任务状态、元数据变更记录,故障排查时间从 30 分钟缩短至 5 分钟。

2.3 稳定:保障数据一致性与作业可用性

  • 数据一致性保障:Flink 内置 Exactly-Once 语义,Dinky 批流统一 SQL 语法,确保批流数据口径一致,结果偏差率从 5% 降至 0.1%;
  • 作业高可用:DolphinScheduler 分布式调度架构、Flink 集群 HA 配置,保障单点故障不影响整体作业运行,作业可用性从 99% 提升至 99.99%;
  • 元数据安全:Dinky 支持 MySQL/Hive Catalog 持久化,元数据存储在数据库中,集群重启后无丢失,元数据可靠性达 100%。

2.4 扩展:支持多场景适配,架构弹性伸缩

  • 多数据源兼容:Dinky 内置 Kafka、MySQL、Hive、ClickHouse、Elasticsearch 等 20 + 数据源连接器,新增数据源无需开发,适配时间从 3 天缩短至 10 分钟;
  • 架构弹性扩展:Flink 与 DolphinScheduler 均支持水平扩展,新增 Worker 节点即可提升计算与调度能力,支持 1000 + 作业并发执行;
  • 业务场景覆盖:支持电商实时大屏、金融风控预警、IoT 设备监控、实时推荐等多场景,一套架构满足企业多样化数据需求。

三、选型对比:为何选择 Flink+DolphinScheduler+Dinky 组合?

实时数仓技术栈选型需覆盖 "计算引擎、调度工具、开发平台" 三大核心模块,需从 "功能适配性、易用性、扩展性、性能" 四个维度对比主流方案,最终确定最优组合。

计算引擎是实时数仓的 "心脏",需具备低延迟、高吞吐、强一致性的特性,三者对比如下:

维度 Apache Flink Spark Streaming Kafka Streams 选型结论
处理模式 原生流处理(基于事件时间),支持毫秒级延迟 微批处理(最小批次 100ms+),延迟较高 原生流处理(基于事件时间),支持毫秒级延迟 Flink/Kafka Streams 更适合低延迟场景;但 Kafka Streams 仅支持 Kafka 数据源,适配性弱,最终选 Flink
状态管理 内置状态后端(Memory/RocksDB),支持 Exactly-Once 语义 需依赖外部存储(Redis/HBase)实现状态管理,语义保障弱 内置状态管理(基于 Kafka 分区),支持 Exactly-Once 语义 Flink 状态管理更成熟,支持复杂状态(如窗口聚合、CEP),无需依赖外部组件,降低架构复杂度
批流一体支持 支持流批统一 API(DataStream API),代码可复用 批流 API 分离(Spark Core/Spark Streaming),需维护两套代码 仅支持流处理,无批处理能力 Flink 适配 "批流一体数仓",减少代码冗余,避免批流结果偏差
数据源与连接器 原生支持 Kafka、MySQL、Hive、ClickHouse 等 20 + 数据源 支持主流数据源,但流处理连接器更新慢(如 ClickHouse 连接器需第三方扩展) 仅支持 Kafka 数据源,无法接入其他存储 Flink 数据源覆盖最广,无需自定义开发,适配企业多数据源需求
窗口计算能力 支持滚动、滑动、会话、会话窗口,支持延迟数据处理(Watermark) 仅支持基于时间的滚动 / 滑动窗口,延迟数据处理能力弱 支持滚动、滑动窗口,无会话窗口,延迟数据处理需自定义 Flink 窗口功能最完善,满足复杂业务场景(如 "用户 30 分钟内连续点击" 会话分析)
社区生态与文档 Apache 顶级项目,社区活跃,文档完善,中文资料丰富 Apache 顶级项目,社区活跃,但流处理文档较简略 Confluent 维护,社区规模较小,中文资料少 Flink 生态最成熟,问题排查、技术支持更便捷,降低运维成本
典型应用场景 实时大屏、金融风控、IoT 监控、批流一体数仓 批流分离场景、非低延迟需求的实时分析(如小时级报表) 简单 Kafka 数据处理(如数据过滤、格式转换) Flink 覆盖场景最广,适配企业复杂实时数仓需求

结论:Flink 在处理模式、状态管理、批流一体、数据源适配等方面均优于 Spark Streaming 与 Kafka Streams,是实时数仓计算引擎的最优选择。

3.2 调度工具选型:DolphinScheduler vs Airflow vs Azkaban

调度工具是实时数仓的 "大脑",需具备分布式调度、可视化编排、高可用、告警联动的特性,三者对比如下:

维度 DolphinScheduler Apache Airflow Azkaban 选型结论
架构设计 原生分布式(Master-Worker),支持多 Master/Worker 部署,水平扩展能力强 单 Master 架构,分布式需依赖 Kubernetes/YARN,配置复杂 单 Server 架构,无分布式能力,仅支持单机调度 DolphinScheduler 分布式架构更适合大规模数仓,支持 1000 + 作业并发,避免单点故障
工作流编排 可视化拖拽界面,支持分支、循环、条件判断,非技术人员可操作 需通过 Python 代码定义 DAG( Directed Acyclic Graph ),学习成本高 可视化拖拽界面,但仅支持线性依赖,无分支 / 循环逻辑 DolphinScheduler 编排最灵活,易用性最高,适合开发 / 运维 / 业务人员协作
任务类型支持 内置 Dinky、Flink、Spark、Shell、SQL 等 20 + 任务类型,支持自定义任务 支持主流任务类型,但需通过插件扩展(如 Dinky 任务需自定义 Operator) 仅支持 Shell、Hive、Pig 任务,扩展能力弱 DolphinScheduler 内置 Dinky/Flink 任务类型,无需二次开发,与计算引擎联动更紧密
调度触发方式 支持时间触发(Cron)、事件触发(上游任务完成)、手动触发,触发逻辑灵活 主要支持时间触发,事件触发需依赖外部组件(如 Apache NiFi) 仅支持时间触发与手动触发,无事件触发 DolphinScheduler 触发方式最丰富,满足 "数据就绪后自动执行" 的实时场景需求
告警与监控 支持邮件、企业微信、钉钉、短信告警,内置任务失败重试、超时告警,监控界面直观 告警需依赖第三方集成(Prometheus+Grafana),配置复杂,无内置重试机制 仅支持邮件告警,无重试机制,监控功能简单 DolphinScheduler 告警与监控最完善,减少故障响应时间,降低运维成本
多租户与权限控制 支持多项目隔离,细粒度权限控制(如 "只读""编辑""执行"),通过 Token 实现跨系统权限联动 支持多租户,但权限控制较粗(仅项目级),无 Token 联动机制 无多租户概念,权限控制仅支持用户级,无法隔离项目 DolphinScheduler 权限体系更完善,适合多团队协作的企业场景
社区支持与文档 国内团队主导开发,中文文档完善,社区响应快(issue 24 小时内回复) 国外团队主导,中文文档较少,社区响应较慢(issue 1-3 天回复) 社区活跃度低,文档陈旧,新版本更新慢 DolphinScheduler 更适合国内企业,技术支持与问题排查更便捷
部署与维护成本 基于 Spring Boot 开发,部署简单(仅需 Java 环境),维护成本低 依赖 Python 环境,需配置 Celery、Redis 等组件,部署复杂 部署简单,但无分布式能力,维护成本随作业量增加而上升 DolphinScheduler 部署维护成本最低,适合中小企业与大型企业

结论:DolphinScheduler 在分布式架构、可视化编排、任务类型支持、告警监控等方面均优于 Airflow 与 Azkaban,是实时数仓调度工具的最优选择。

开发平台是实时数仓的 "工作台",需具备低代码开发、元数据管理、全生命周期监控、调度联动的特性,三者对比如下:

维度 Dinky Flink SQL Client HUE(Hadoop User Experience) 选型结论
开发体验 可视化 FlinkSQL Studio,支持语法提示、格式化、代码高亮、SQL 模板,支持多标签页编辑,开发效率高 纯命令行交互,无语法提示 / 格式化功能,需手动输入完整 SQL 语句,开发体验差 支持 Hive SQL 可视化编辑,但对 Flink SQL 支持有限(仅基础语法高亮,无调试功能) Dinky 开发体验最优,大幅降低 Flink SQL 开发门槛,非开发人员也可快速上手
Flink 版本兼容性 支持 Flink 1.15~1.19 多版本,无需手动切换集群配置,可在同一界面开发不同版本作业 仅支持当前绑定的 Flink 版本,切换版本需重新部署 Client,操作繁琐 依赖外部 Flink 集成,版本兼容性弱,常出现 "语法支持不全" 问题 Dinky 多版本兼容能力满足企业 "逐步升级 Flink 集群" 的需求,避免版本切换成本
元数据管理 支持 MySQL/Hive Catalog 持久化(表结构、UDF、数据源、函数),元数据变更可追溯,集群重启不丢失 元数据存储在内存,会话结束 / 集群重启后完全丢失,需重新执行 DDL 仅支持 Hive 元数据管理,无 Flink 专属元数据模块,无法存储 UDF 与作业配置 Dinky 元数据管理最完善,解决传统架构 "元数据碎片化" 痛点,减少重复建表操作
作业调试与运维 支持一键调试(本地 / 远程模式)、实时查看作业日志、Flink UI 跳转、作业暂停 / 重启 / 终止,全生命周期可控 调试需依赖 Flink WebUI,日志需手动登录集群查看,作业运维操作繁琐 无 Flink 作业调试功能,仅支持查看 Hive 作业日志,运维能力弱 Dinky 运维功能最全面,无需切换多工具,实现 "开发 - 调试 - 运维" 一体化
UDF 开发与管理 支持 Python/Java/Scala UDF 开发,提供模板生成(如 ScalarFunction 继承类)、一键发布、版本管理,支持 UDF 血缘分析 需手动通过 JAR 包注册 UDF,无版本管理,UDF 冲突需手动排查 不支持 Flink UDF 开发,仅支持 Hive UDF 注册,功能单一 Dinky UDF 管理能力满足企业 "自定义函数复用" 需求,降低 UDF 维护成本
调度工具联动 内置 DolphinScheduler 集成模块,支持作业 "发布→推送→调度" 一键操作,无需手动在调度平台创建任务 无调度联动功能,需手动通过 Shell 脚本调用 Client,再配置调度任务 仅支持与 Azkaban 简单联动,无 DolphinScheduler 集成能力 Dinky 与 DolphinScheduler 深度集成,打通 "开发 - 调度" 链路,减少手动操作
批流一体支持 内置批流统一 SQL 语法,支持动态切换执行模式(流模式 / 批模式),作业代码可复用 需手动指定执行模式(execution.type=streaming/batch),批流语法需单独适配 仅支持批处理(Hive),无流处理能力,无法适配批流一体架构 Dinky 批流一体支持能力保障数据口径一致,避免 "一套逻辑两套代码"
社区支持与更新 开源项目活跃,迭代速度快(每月更新小版本,每季度更新大版本),中文文档完善,社区群实时答疑 作为 Flink 附属工具,更新缓慢,功能迭代依赖 Flink 主版本 社区活跃度低,近 2 年无重大版本更新,功能停滞 Dinky 社区支持最及时,可快速获取新功能与问题修复,保障平台稳定性

结论:Dinky 在开发体验、元数据管理、运维能力、调度联动等方面全面超越 Flink SQL Client 与 HUE,是 Flink 作业开发与数仓管理的最优平台选择。

3.4 整体组合优势总结

"Flink+DolphinScheduler+Dinky" 组合并非简单的组件叠加,而是形成 "计算 - 调度 - 开发" 三位一体的协同体系,核心优势如下:

四、系统架构:自动化数仓平台的整体设计

本平台采用 "分层解耦、分布式部署" 架构,遵循 "高可用、可扩展、易维护" 原则,自上而下分为接入层、开发层、调度层、计算层、存储层、监控层,各层独立部署、协同工作,具体架构如下:

4.1 架构分层与组件部署

4.1.1 架构分层图
4.1.2 各层核心组件与部署要求
分层 核心组件 部署模式 硬件配置建议(单节点) 核心职责
接入层 Kafka、Flink CDC、DataX 分布式(Kafka 3 节点 +) CPU:4 核,内存:8G,磁盘:100G 实现多数据源实时 / 批同步,将原始数据统一接入平台,保持数据原貌
开发层 Dinky 平台 单机 / 集群(生产建议集群) CPU:4 核,内存:8G,磁盘:50G 提供 Flink 作业开发、元数据管理、UDF 开发能力,是平台的 "开发入口"
调度层 DolphinScheduler 分布式(Master 2 节点 + Worker 3 节点 +) CPU:4 核,内存:8G,磁盘:50G 负责作业调度、任务依赖管理、告警发送,是平台的 "调度中枢"
计算层 Flink 集群 分布式(JobManager 2 节点 + TaskManager 3 节点 +) CPU:8 核,内存:16G,磁盘:100G 执行实时 / 批计算任务,处理数据清洗、汇总、分析逻辑,是平台的 "计算核心"
存储层 MySQL、Hive、ClickHouse 分布式(Hive/ClickHouse 3 节点 +) CPU:8 核,内存:16G,磁盘:500G 存储计算结果(实时结果→MySQL/ClickHouse,批结果→Hive)与元数据
监控层 Prometheus、Grafana、Kibana 分布式(Prometheus 2 节点 +) CPU:4 核,内存:8G,磁盘:100G 监控全链路组件状态、作业运行情况、日志,及时发现故障并告警

4.2 核心技术特性设计

4.2.1 高可用设计

为避免单点故障,平台各核心组件均采用高可用部署,具体方案如下:

4.2.2 扩展性设计

平台支持水平扩展,可根据业务需求灵活增加节点,具体扩展方案如下:

4.2.3 安全性设计

平台从 "权限控制、数据加密、日志审计" 三个维度保障安全性:

4.3 核心业务流程示例(电商实时销量大屏)

以 "电商实时销量大屏" 业务为例,完整展示平台从数据接入到结果展示的全流程,具体步骤如下:

步骤 1:数据接入(接入层)
  • 实时数据源 :用户下单数据通过业务系统写入 Kafka 主题 order_real_time(字段:order_iduser_idamountpay_timestatus);
  • 数据同步 :通过 Flink CDC 同步 MySQL 商品表 product_info(字段:product_idproduct_namecategory)至 Kafka 主题 product_real_time
  • 数据格式:Kafka 消息格式为 JSON,确保数据结构标准化,便于后续处理。
步骤 2:作业开发(开发层)
  1. 数据源配置 :在 Dinky 平台 "数据源管理" 模块,新增 Kafka 数据源(配置 bootstrap.servers=kafka-1:9092,kafka-2:9092)与 MySQL 数据源(配置商品表连接信息);

  2. 元数据持久化 :创建 MySQL Catalog ecommerce_catalog,用于存储后续创建的表结构与 UDF;

    sql 复制代码
    CREATE CATALOG ecommerce_catalog WITH (
      'type' = 'dinky_mysql',
      'username' = 'dinky',
      'password' = 'dinky@123',
      'url' = 'jdbc:mysql://mysql-1:3306/dinky_metadata?useSSL=false&serverTimezone=Asia/Shanghai'
    );
    USE CATALOG ecommerce_catalog;
  3. 创建表结构 :在 Dinky 中编写 Flink SQL,创建 Kafka 源表与 ClickHouse 结果表;

    sql

    sql 复制代码
    -- 1. 创建 Kafka 订单源表
    CREATE TABLE order_kafka_source (
      `order_id` BIGINT COMMENT '订单ID',
      `user_id` BIGINT COMMENT '用户ID',
      `product_id` BIGINT COMMENT '商品ID',
      `amount` DECIMAL(10,2) COMMENT '订单金额',
      `pay_time` TIMESTAMP_LTZ(3) COMMENT '支付时间',
      `status` STRING COMMENT '订单状态(PAID/UNPAID)'
    ) WITH (
      'connector' = 'kafka',
      'topic' = 'order_real_time',
      'properties.bootstrap.servers' = '${kafka_source.bootstrap.servers}', -- 引用 Dinky 数据源变量
      'properties.group.id' = 'order_consumer_group',
      'scan.startup.mode' = 'latest-offset',
      'format' = 'json',
      'json.ignore-parse-errors' = 'true'
    );
    
    -- 2. 创建 Kafka 商品源表
    CREATE TABLE product_kafka_source (
      `product_id` BIGINT COMMENT '商品ID',
      `product_name` STRING COMMENT '商品名称',
      `category` STRING COMMENT '商品分类'
    ) WITH (
      'connector' = 'kafka',
      'topic' = 'product_real_time',
    'properties.bootstrap.servers' = '${kafka_source.bootstrap.servers}',
    'properties.group.id' = 'product_consumer_group',
    'scan.startup.mode' = 'earliest-offset',
    'format' = 'json',
    'json.ignore-parse-errors' = 'true'
    );
    -- 3. 创建 ClickHouse 实时销量结果表(按分类汇总)
    CREATE TABLE sales_clickhouse_sink (
    category STRING COMMENT ' 商品分类 ',
    real_time_sales DECIMAL (12,2) COMMENT ' 实时销量金额 ',
    order_count BIGINT COMMENT ' 订单数量 ',
    update_time TIMESTAMP COMMENT ' 更新时间 ',
    PRIMARY KEY (category, update_time) NOT ENFORCED -- 复合主键确保数据唯一性
    ) WITH (
    'connector' = 'clickhouse',
    'url' = 'jdbc:clickhouse://clickhouse-1:8123,ecommerce_sales',
    'table-name' = 'real_time_sales_summary',
    'username' = 'clickhouse',
    'password' = 'clickhouse@123',
    'sink.batch-size' = '1000', -- 批量写入大小
    'sink.flush-interval' = '1000' -- 刷新间隔(毫秒)
    );

4. **开发 UDF 函数**

sql 复制代码
 **开发 UDF 函数**:创建 Python UDF `format_amount`,用于格式化订单金额(保留 2 位小数并添加千分位分隔符);
- 选择 Dinky 作业类型为"Python",模板为"UDF / python_udf_1";
- 编写 UDF 代码:
  ```python
  from pyflink.table import ScalarFunction, DataTypes
  from pyflink.table.udf import udf

  class format_amount(ScalarFunction):
      def __init__(self):
          pass

      def eval(self, amount: float) -> str:
          # 格式化金额:保留2位小数,添加千分位分隔符
          return "{:,.2f}".format(amount)

  # 注册 UDF,指定返回类型为字符串
  format_amount_udf = udf(format_amount(), result_type=DataTypes.STRING())

编写计算逻辑:关联订单表与商品表,按商品分类实时汇总销量,并调用 UDF 格式化金额;

sql 复制代码
-- 1. 关联订单与商品数据,过滤已支付订单
WITH order_product_join AS (
  SELECT 
    o.order_id,
    o.user_id,
    o.amount,
    o.pay_time,
    p.product_name,
    p.category
  FROM order_kafka_source o
  JOIN product_kafka_source p ON o.product_id = p.product_id
  WHERE o.status = 'PAID' -- 仅处理已支付订单
),

-- 2. 按分类实时汇总(5秒滚动窗口)
category_sales_summary AS (
  SELECT 
    category,
    SUM(amount) AS real_time_sales,
    COUNT(order_id) AS order_count,
    CURRENT_TIMESTAMP() AS update_time
  FROM order_product_join
  GROUP BY category, TUMBLE(pay_time, INTERVAL '5' SECOND) -- 5秒滚动窗口
)

-- 3. 写入 ClickHouse 结果表,同时调用 UDF 格式化金额(仅用于日志打印)
INSERT INTO sales_clickhouse_sink
SELECT 
  category,
  real_time_sales,
  order_count,
  update_time
FROM category_sales_summary;

-- 打印格式化后的金额(用于调试)
SELECT 
  category,
  format_amount_udf(real_time_sales) AS formatted_sales,
  order_count,
  update_time
FROM category_sales_summary;

作业调试:选择 Flink 实例(外部分布式集群),点击 "调试" 按钮,查看日志确认无语法错误,数据可正常读取与写入。

步骤 3:调度配置(调度层)

Dinky 与 DolphinScheduler 联动配置

在 DolphinScheduler 中创建令牌:进入 "安全中心→令牌管理",创建令牌 dinky_token,设置有效期为 1 年(避免频繁过期);

在 Dinky 中配置调度信息:进入 "配置中心→全局配置→DolphinScheduler 配置",启用开关,填写 DolphinScheduler 地址(http://dolphinscheduler-master-1:12345)、项目名称(ecommerce_realtime)、令牌 dinky_token

作业发布与推送

在 Dinky 中发布 Flink 作业(版本号 V1.0),点击 "推送" 按钮,选择推送至 DolphinScheduler 项目 ecommerce_realtime

推送完成后,在 DolphinScheduler 项目中自动生成工作流 sales_summary_workflow,包含 1 个 Dinky 任务(关联已发布的 Flink 作业);

调度规则配置

编辑工作流 sales_summary_workflow,设置调度周期为 "连续运行"(实时作业需持续执行);

配置依赖规则:添加 "Kafka 主题健康检查" 前置任务(Shell 脚本检测 order_real_time 主题是否可用),确保上游数据就绪后再执行 Flink 作业;

配置告警规则:任务失败时触发 "企业微信 + 邮件" 双告警,通知运维团队(接收人:ops_team@xxx.com),并设置失败重试(重试次数 2 次,间隔 5 分钟)。

步骤 4:计算执行(计算层)
  • Flink 作业提交:DolphinScheduler 按调度规则触发任务,通过 Dinky API 向 Flink 集群提交作业;
  • Flink 执行流程
    • JobManager 接收作业,解析 Flink SQL 生成物理执行计划,分配资源(每个 TaskManager 分配 2 核 CPU、4G 内存);
    • TaskManager 启动 Task 执行数据处理:
      • Source Task:读取 Kafka 主题 order_real_timeproduct_real_time 数据;
      • Join Task:关联订单与商品数据,过滤已支付订单;
      • Aggregate Task:按 5 秒滚动窗口汇总各分类销量;
      • Sink Task:将汇总结果写入 ClickHouse 表 real_time_sales_summary
    • 状态管理:采用 RocksDBStateBackend,窗口聚合状态持久化至 HDFS(路径 hdfs://hdfs-nn:9000/flink/state/sales_summary),避免作业重启后状态丢失;
  • 作业监控 :通过 Flink WebUI(http://flink-jobmanager-1:8081)查看作业运行状态,确认 Task 无失败,数据吞吐率正常(约 1000 条 / 秒)。
步骤 5:结果存储与应用(存储层 + 应用层)
  • 结果存储 :Flink 作业将实时销量汇总结果写入 ClickHouse 表 real_time_sales_summary,每 5 秒更新一次数据;
  • 数据应用 :电商实时大屏通过 JDBC 连接 ClickHouse,查询 real_time_sales_summary 表数据,展示各商品分类的实时销量金额、订单数量,并按销量排序 Top5 分类,支持钻取查看具体商品销量(关联 product_info 表)。
步骤 6:全链路监控(监控层)
  • 组件监控:Prometheus 采集 Flink、DolphinScheduler、Dinky 组件指标(如 Flink TaskManager CPU 使用率、DolphinScheduler 任务成功率),Grafana 配置仪表盘展示,设置阈值告警(如 Flink Task 失败率 > 0 时告警);
  • 作业监控:Dinky 内置监控模块展示作业运行时长、数据读取 / 写入量、UDF 调用次数,支持查看实时日志;Flink WebUI 查看 Task 背压情况(若背压高,需增加 TaskManager 节点);
  • 日志监控:全链路日志(Dinky 开发日志、Flink 执行日志、DolphinScheduler 调度日志)统一写入 Elasticsearch,通过 Kibana 按 "作业 ID""时间范围" 查询,快速定位故障(如 "2024-05-20 14:30 作业失败" 可通过日志发现是 Kafka 连接超时)。

五、数据架构:批流一体的数仓分层设计

基于 Flink 批流一体能力与 Dinky 元数据管理特性,本平台采用 "Lambda 架构优化版" 设计数据分层,兼顾实时性与数据完整性,同时避免传统 Lambda 架构 "批流分离" 的痛点。数据架构从下至上分为 ODS 层(操作数据存储层)、DWD 层(数据仓库明细层)、DWS 层(数据仓库汇总层)、ADS 层(数据应用层),各层职责清晰、数据单向流动,具体设计如下:

5.1 数据分层详情

分层 英文全称 定位 存储组件 数据处理规则 典型表 / 主题示例 数据时效
ODS 层 Operational Data Store 原始数据接入层,"数据着陆区" Kafka(实时)、HDFS(批) 1. 保持数据原貌,不做清洗 / 转换; 2. 按数据源分区存储(如 Kafka 按主题、HDFS 按日期); 3. 保留原始格式(JSON/CSV/Parquet) 实时:ods_kafka_order(订单日志)、ods_kafka_product(商品日志); 批:ods_hdfs_user(用户全量数据) 实时(毫秒级)、批(T+1)
DWD 层 Data Warehouse Detail 数据清洗层,"明细数据层" Kafka(实时)、Hive(批) 1. 数据清洗:去重(如订单重复日志)、补空(如缺失的商品分类)、格式标准化(如时间统一为 UTC+8); 2. 数据脱敏:敏感字段加密(如用户手机号脱敏为 138****5678); 3. 按业务主题拆分(如 "订单主题""用户主题") 实时:dwd_kafka_order_detail(订单明细)、dwd_kafka_user_behavior(用户行为明细); 批:dwd_hive_product_detail(商品明细) 实时(秒级)、批(T+1)
DWS 层 Data Warehouse Summary 数据汇总层,"汇总数据层" ClickHouse(实时)、Hive(批) 1. 实时汇总:按时间窗口(如 5 秒 / 1 分钟)、业务维度(如商品分类、区域)聚合; 2. 批处理补全:每日凌晨执行批作业,补全前一天全量数据(修正实时数据偏差); 3. 数据去重:基于主键(如 "订单 ID + 时间")确保汇总结果唯一性 实时:dws_clickhouse_sales_real(实时销量汇总)、dws_clickhouse_user_active(实时用户活跃汇总); 批:dws_hive_sales_day(日销量汇总) 实时(秒级 / 分钟级)、批(T+1)
ADS 层 Application Data Store 数据应用层,"业务输出层" MySQL(轻量应用)、ClickHouse(OLAP)、Redis(缓存) 1. 面向业务需求封装数据(如实时大屏需 "分类销量 Top10"、风控需 "高风险订单列表"); 2. 数据压缩:减少冗余字段,仅保留业务所需字段; 3. 支持高并发查询(如 MySQL 加索引、Redis 缓存热点数据) 实时大屏:ads_mysql_sales_top10(销量 Top10 分类); 风控:ads_clickhouse_risk_order(高风险订单); 缓存:ads_redis_user_active(实时活跃用户数) 实时(毫秒级 / 秒级)、批(T+1)

5.2 批流一体设计核心策略

传统 Lambda 架构中,批处理层(Batch Layer)与流处理层(Speed Layer)独立运行,易导致数据口径不一致。本平台通过以下策略实现 "批流一体",确保实时数据与批数据结果统一:

5.2.1 统一数据模型
  • 表结构统一 :DWD/DWS/ADS 层的实时表与批表采用相同的表结构(字段名、数据类型、主键),例如:
    • 实时表 dws_clickhouse_sales_real 与批表 dws_hive_sales_day 均包含 category(商品分类)、sales_amount(销量金额)、stat_date(统计日期)字段;
  • 维度统一 :批流共享同一维度表(如商品维度表 dwd_hive_product_dim),实时作业通过 Flink Hive Catalog 读取批维度表,避免 "实时用临时维度、批用正式维度" 导致的偏差。
5.2.2 统一计算逻辑
  • SQL 语法统一 :基于 Dinky 批流统一 SQL 语法,实时作业与批作业复用同一套计算逻辑,仅通过 execution.type 参数切换执行模式:

    sql

    sql 复制代码
    -- 实时模式(流处理)
    SET 'execution.type' = 'streaming';
    -- 批模式(批处理)
    SET 'execution.type' = 'batch';
    
    -- 统一计算逻辑:按分类汇总销量
    SELECT 
      category,
      SUM(amount) AS sales_amount,
      COUNT(order_id) AS order_count,
      DATE_FORMAT(pay_time, 'yyyy-MM-dd') AS stat_date
    FROM dwd_layer_table
    WHERE status = 'PAID'
    GROUP BY category, DATE_FORMAT(pay_time, 'yyyy-MM-dd');
  • 函数统一 :批流作业调用相同的 UDF(如 format_amount 金额格式化函数),避免 "实时用 Python UDF、批用 Hive UDF" 导致的计算差异。

5.2.3 数据补全与合并
  • 实时数据补全:实时作业通过 Flink 窗口延迟处理(Watermark)接收迟到数据(如用户支付延迟 5 分钟),减少实时数据遗漏;
  • 批流数据合并:每日凌晨(如 2:00)执行批作业,计算前一天全量销量数据,覆盖实时作业的汇总结果(修正实时数据因 "迟到数据未处理" 导致的偏差),确保最终结果一致性;
  • 合并策略示例

批作业计算 dws_hive_sales_day(T-1 日全量销量);

通过 Flink 批作业将 dws_hive_sales_day 数据覆盖写入 ClickHouse 实时表 dws_clickhouse_sales_real 中 T-1 日的分区;

应用层查询时,T-1 日及之前的数据读取批处理结果,当日数据读取实时结果,实现 "批流无缝衔接"。

5.3 数据流转示例(电商销量分析)

以 "电商销量分析" 业务为例,结合批流一体设计策略,完整展示数据从 ODS 层到 ADS 层的流转过程、处理逻辑及技术实现,清晰呈现各层数据的加工链路与价值转化。

5.3.1 流转链路概览

数据流转遵循 "单向流动、分层加工" 原则,实时链路与批处理链路并行且最终融合,具体链路如下:

  • 实时链路:ODS 层(Kafka 实时订单 / 商品日志)→ DWD 层(Kafka 清洗后明细)→ DWS 层(ClickHouse 实时汇总)→ ADS 层(MySQL/Redis 应用数据),支撑实时销量大屏、实时库存调整等低延迟需求;
  • 批处理链路:ODS 层(HDFS 全量订单 / 用户数据)→ DWD 层(Hive 明细补全)→ DWS 层(Hive 日汇总)→ DWS 层(ClickHouse 历史数据覆盖),修正实时数据偏差、补全历史数据,支撑财务报表、月度销量分析等精准性需求。
5.3.2 各层数据处理详情

1. ODS 层:原始数据接入

作为数据 "着陆区",ODS 层不做任何业务加工,仅按数据源类型存储原始数据,确保数据溯源能力。

类型 数据源 存储组件 数据格式 分区 / 主题设计 数据内容示例
实时订单 电商业务系统(下单接口) Kafka JSON 主题:ods_kafka_order_real_time,按 "小时" 分区(如 2024052014 {"order_id":10001,"user_id":20005,"product_id":30012,"amount":999.00,"pay_time":"2024-05-20 14:30:25","status":"UNPAID","create_time":"2024-05-20 14:29:58"}
实时商品 Flink CDC(MySQL 商品表) Kafka JSON 主题:ods_kafka_product_real_time,按 "天" 分区(如 20240520 {"product_id":30012,"product_name":"智能手表","category":"数码产品","price":1299.00,"stock":500,"update_time":"2024-05-20 10:15:30"}
批订单 业务系统每日全量导出 HDFS(Hive 外部表) Parquet Hive 分区:dt=20240520(按日期),分桶:order_id%10(10 个桶) 同实时订单结构,包含当日所有订单(含历史未支付转支付订单)
批用户 用户中心全量同步 HDFS(Hive 外部表) Parquet Hive 分区:dt=20240520,分桶:user_id%5(5 个桶) {"user_id":20005,"user_name":"张三","phone":"138****5678","region":"北京","register_time":"2023-01-15 09:20:30"}

技术实现

  • 实时数据:业务系统通过 Kafka Producer 写入订单主题,Flink CDC 监听 MySQL 商品表 binlog,自动同步变更数据至商品主题;
  • 批数据:业务系统每日 00:30 导出前一日全量订单 CSV 文件,通过 DataX 同步至 HDFS,Hive 外部表关联 HDFS 路径实现查询。

2. DWD 层:数据清洗与标准化

DWD 层是 "数据净化站",通过清洗、脱敏、标准化处理,将 ODS 层原始数据转化为结构化、高质量的明细数据,为后续汇总提供统一基础。

(1)实时 DWD 处理(Kafka 明细)

处理逻辑:基于 Flink SQL 实时处理 ODS 层 Kafka 数据,核心操作包括:

  • 数据清洗 :过滤无效订单(如 order_id 为空、amount ≤0)、去重(基于 order_id + create_time 主键,避免重复日志);
  • 字段补全 :对缺失的 category(商品分类),通过关联商品主题数据补全;
  • 格式标准化 :将 pay_time/create_time 统一转换为 UTC+8 时区的 TIMESTAMP_LTZ(3) 类型;
  • 敏感数据脱敏 :用户手机号 phone 字段脱敏(如 138****5678),通过 Dinky 内置 UDF mask_phone 实现。

SQL 实现(Dinky 平台)

sql 复制代码
-- 1. 读取 ODS 层订单与商品 Kafka 数据
CREATE TABLE ods_kafka_order (
  `order_id` BIGINT COMMENT '订单ID',
  `user_id` BIGINT COMMENT '用户ID',
  `product_id` BIGINT COMMENT '商品ID',
  `amount` DECIMAL(10,2) COMMENT '订单金额',
  `pay_time` TIMESTAMP_LTZ(3) COMMENT '支付时间',
  `status` STRING COMMENT '订单状态',
  `create_time` TIMESTAMP_LTZ(3) COMMENT '创建时间',
  `proc_time` AS PROCTIME() COMMENT '处理时间(用于水印)'
) WITH (
  'connector' = 'kafka',
  'topic' = 'ods_kafka_order_real_time',
  'properties.bootstrap.servers' = '${kafka_servers}',
  'properties.group.id' = 'dwd_order_consumer',
  'scan.startup.mode' = 'latest-offset',
  'format' = 'json'
);

CREATE TABLE ods_kafka_product (
  `product_id` BIGINT COMMENT '商品ID',
  `product_name` STRING COMMENT '商品名称',
  `category` STRING COMMENT '商品分类',
  `update_time` TIMESTAMP_LTZ(3) COMMENT '更新时间'
) WITH (
  'connector' = 'kafka',
  'topic' = 'ods_kafka_product_real_time',
  'properties.bootstrap.servers' = '${kafka_servers}',
  'properties.group.id' = 'dwd_product_consumer',
  'scan.startup.mode' = 'earliest-offset',
  'format' = 'json'
);

-- 2. 创建 DWD 层订单明细 Kafka 表
CREATE TABLE dwd_kafka_order_detail (
  `order_id` BIGINT COMMENT '订单ID',
  `user_id` BIGINT COMMENT '用户ID',
  `user_phone_masked` STRING COMMENT '脱敏手机号(关联用户表获取)',
  `product_id` BIGINT COMMENT '商品ID',
  `product_name` STRING COMMENT '商品名称',
  `category` STRING COMMENT '商品分类',
  `amount` DECIMAL(10,2) COMMENT '订单金额',
  `pay_time` TIMESTAMP_LTZ(3) COMMENT '支付时间',
  `status` STRING COMMENT '订单状态',
  `create_time` TIMESTAMP_LTZ(3) COMMENT '创建时间',
  PRIMARY KEY (`order_id`) NOT ENFORCED -- 主键去重
) WITH (
  'connector' = 'kafka',
  'topic' = 'dwd_kafka_order_detail',
  'properties.bootstrap.servers' = '${kafka_servers}',
  'format' = 'json',
  'sink.partitioner' = 'round-robin' -- 轮询分区,负载均衡
);

-- 3. 清洗并写入 DWD 层(关联商品表补全分类,脱敏手机号)
INSERT INTO dwd_kafka_order_detail
SELECT 
  o.order_id,
  o.user_id,
  mask_phone(u.phone) AS user_phone_masked, -- 内置脱敏UDF
  o.product_id,
  p.product_name,
  p.category,
  o.amount,
  o.pay_time,
  o.status,
  o.create_time
FROM ods_kafka_order o
-- 关联商品表(实时维度关联,采用Lookup Join)
LEFT JOIN ods_kafka_product FOR SYSTEM_TIME AS OF o.proc_time p 
  ON o.product_id = p.product_id
-- 关联批用户表(Hive 维度表,通过Flink Hive Catalog读取)
LEFT JOIN hive_catalog.ods_db.ods_hive_user u 
  ON o.user_id = u.user_id
-- 过滤无效数据
WHERE o.order_id IS NOT NULL 
  AND o.amount > 0 
  AND o.status IN ('PAID', 'UNPAID', 'REFUNDED');

(2)批 DWD 处理(Hive 明细)

处理逻辑:每日凌晨 01:00 执行 Flink 批作业,基于 ODS 层 Hive 全量数据,对实时 DWD 数据进行补全(如遗漏的历史订单、延迟同步的用户数据),核心操作包括:

  1. 补全历史数据 :对实时链路未捕获的前一日未支付转支付订单,更新 status 字段;

  2. 修正数据偏差 :若实时关联商品表时出现延迟,批处理重新关联最新商品维度表,修正 category 字段;

  3. 数据对齐:确保批处理明细与实时明细字段完全一致,为后续批流汇总统一基础。

SQL 实现(Dinky 平台,批模式)

sql 复制代码
-- 1. 切换为批处理模式
SET 'execution.type' = 'batch';

-- 2. 读取 ODS 层批订单与批用户 Hive 表
CREATE TABLE ods_hive_order (
  `order_id` BIGINT COMMENT '订单ID',
  `user_id` BIGINT COMMENT '用户ID',
  `product_id` BIGINT COMMENT '商品ID',
  `amount` DECIMAL(10,2) COMMENT '订单金额',
  `pay_time` TIMESTAMP COMMENT '支付时间',
  `status` STRING COMMENT '订单状态',
  `create_time` TIMESTAMP COMMENT '创建时间',
  `dt` STRING COMMENT '分区日期'
) WITH (
  'connector' = 'hive',
  'database-name' = 'ods_db',
  'table-name' = 'ods_hive_order',
  'partition' = 'dt=20240520' -- 处理前一日数据
);

-- 3. 创建 DWD 层批订单明细 Hive 表
CREATE TABLE dwd_hive_order_detail (
  `order_id` BIGINT COMMENT '订单ID',
  `user_id` BIGINT COMMENT '用户ID',
  `user_phone_masked` STRING COMMENT '脱敏手机号',
  `product_id` BIGINT COMMENT '商品ID',
  `product_name` STRING COMMENT '商品名称',
  `category` STRING COMMENT '商品分类',
  `amount` DECIMAL(10,2) COMMENT '订单金额',
  `pay_time` TIMESTAMP COMMENT '支付时间',
  `status` STRING COMMENT '订单状态',
  `create_time` TIMESTAMP COMMENT '创建时间',
  `dt` STRING COMMENT '分区日期'
) PARTITIONED BY (`dt` STRING)
WITH (
  'connector' = 'hive',
  'database-name' = 'dwd_db',
  'table-name' = 'dwd_hive_order_detail'
);

-- 4. 批处理清洗并写入 DWD 层
INSERT OVERWRITE TABLE dwd_hive_order_detail PARTITION (dt='20240520')
SELECT 
  o.order_id,
  o.user_id,
  mask_phone(u.phone) AS user_phone_masked,
  o.product_id,
  p.product_name,
  p.category,
  o.amount,
  o.pay_time,
  o.status,
  o.create_time
FROM ods_hive_order o
LEFT JOIN hive_catalog.ods_db.ods_hive_product p 
  ON o.product_id = p.product_id
LEFT JOIN hive_catalog.ods_db.ods_hive_user u 
  ON o.user_id = u.user_id
WHERE o.order_id IS NOT NULL 
  AND o.amount > 0;
  1. DWS 层:数据汇总与批流融合

DWS 层是 "数据聚合中心",分别生成实时汇总数据(支撑低延迟需求)和批汇总数据(修正偏差、补全历史),最终通过 "批覆盖实时" 实现数据一致性。

(1)实时 DWS 处理(ClickHouse 汇总)

处理逻辑:基于 DWD 层 Kafka 明细数据,按 "商品分类" 和 "5 秒滚动窗口" 实时聚合销量,核心操作包括:

  1. 窗口聚合:采用 Flink TUMBLE 窗口(5 秒),统计各分类的实时销量金额、订单数量;
  2. 状态管理:使用 RocksDBStateBackend 持久化窗口状态,避免作业重启后数据丢失;
  3. 实时写入:将聚合结果写入 ClickHouse,支持高并发查询(ClickHouse 列存引擎适配 OLAP 场景)。

SQL 实现(Dinky 平台)

sql 复制代码
-- 1. 读取 DWD 层订单明细 Kafka 表
CREATE TABLE dwd_kafka_order_detail (
  -- 字段同 5.3.2.2(1)中定义,此处省略
);

-- 2. 创建 DWS 层实时销量汇总 ClickHouse 表
CREATE TABLE dws_clickhouse_sales_real (
  `category` STRING COMMENT '商品分类',
  `window_start` TIMESTAMP_LTZ(3) COMMENT '窗口开始时间',
  `window_end` TIMESTAMP_LTZ(3) COMMENT '窗口结束时间',
  `real_sales` DECIMAL(12,2) COMMENT '实时销量金额',
  `order_count` BIGINT COMMENT '订单数量',
  `update_time` TIMESTAMP_LTZ(3) COMMENT '数据更新时间',
  PRIMARY KEY (`category`, `window_start`) NOT ENFORCED -- 复合主键确保唯一性
) WITH (
  'connector' = 'clickhouse',
  'url' = 'jdbc:clickhouse://${clickhouse_servers}/dws_db',
  'table-name' = 'dws_clickhouse_sales_real',
  'username' = '${clickhouse_user}',
  'password' = '${clickhouse_pwd}',
  'sink.batch-size' = '500', -- 批量写入,提升性能
  'sink.flush-interval' = '1000' -- 1秒刷新一次
);

-- 3. 实时窗口聚合并写入 ClickHouse
INSERT INTO dws_clickhouse_sales_real
SELECT 
  category,
  TUMBLE_START(pay_time, INTERVAL '5' SECOND) AS window_start,
  TUMBLE_END(pay_time, INTERVAL '5' SECOND) AS window_end,
  SUM(amount) AS real_sales,
  COUNT(DISTINCT order_id) AS order_count, -- 去重统计订单数
  CURRENT_TIMESTAMP() AS update_time
FROM dwd_kafka_order_detail
WHERE status = 'PAID' -- 仅统计已支付订单
GROUP BY category, TUMBLE(pay_time, INTERVAL '5' SECOND);

(2)批 DWS 处理(Hive 汇总 + ClickHouse 覆盖)

处理逻辑:每日凌晨 02:00 执行 Flink 批作业,基于 DWD 层 Hive 明细数据,计算前一日全量销量汇总,核心操作包括:

  1. 全量聚合:按 "商品分类" 和 "日期" 聚合,得到前一日各分类的精准销量(修正实时窗口遗漏的迟到数据);

  2. 历史覆盖:将批汇总结果写入 Hive 表的同时,覆盖 ClickHouse 实时表中对应日期的分区数据,实现 "批流数据统一";

  3. 数据校验:对比批汇总与实时汇总的差异(如差异率 >0.5% 触发告警),确保数据准确性。

SQL 实现(Dinky 平台,批模式)

sql 复制代码
-- 4. 批处理聚合并写入 Hive
INSERT OVERWRITE TABLE dws_hive_sales_day PARTITION (stat_date='20240520')
SELECT 
  category,
  '20240520' AS stat_date,
  SUM(amount) AS day_sales,
  COUNT(DISTINCT order_id) AS day_order_count,
  CURRENT_TIMESTAMP() AS calc_time
FROM dwd_hive_order_detail
WHERE status = 'PAID' 
  AND dt = '20240520' -- 仅处理前一日明细数据
GROUP BY category;

-- 5. 创建 ClickHouse 批数据写入表(复用实时表结构,仅覆盖历史分区)
CREATE TABLE dws_clickhouse_sales_real (
  -- 同 5.3.2.3(1)中实时表结构,此处省略
);

-- 6. 批汇总结果覆盖 ClickHouse 实时表历史数据
INSERT OVERWRITE TABLE dws_clickhouse_sales_real
SELECT 
  category,
  TO_TIMESTAMP_LTZ(CONCAT(stat_date, ' 00:00:00'), 3) AS window_start, -- 按日构建窗口起始时间
  TO_TIMESTAMP_LTZ(CONCAT(stat_date, ' 23:59:59'), 3) AS window_end, -- 按日构建窗口结束时间
  day_sales AS real_sales,
  day_order_count AS order_count,
  CURRENT_TIMESTAMP() AS update_time
FROM dws_hive_sales_day
WHERE stat_date = '20240520';

-- 7. 数据差异校验(对比批汇总与实时汇总结果,差异率>0.5%则告警)
WITH batch_data AS (
  SELECT category, day_sales FROM dws_hive_sales_day WHERE stat_date = '20240520'
),
stream_data AS (
  SELECT 
    category, 
    SUM(real_sales) AS stream_total_sales 
  FROM dws_clickhouse_sales_real
  WHERE DATE_FORMAT(window_start, 'yyyyMMdd') = '20240520'
  GROUP BY category
)
SELECT 
  b.category,
  b.day_sales AS batch_sales,
  s.stream_total_sales,
  ROUND(ABS(b.day_sales - s.stream_total_sales) / b.day_sales * 100, 2) AS diff_rate
FROM batch_data b
JOIN stream_data s ON b.category = s.category
WHERE ROUND(ABS(b.day_sales - s.stream_total_sales) / b.day_sales * 100, 2) > 0.5;
-- 若查询返回结果,通过 Dinky 告警模块触发企业微信通知,提示运维人员排查差异原因

批流融合效果:通过 "实时计算 + 批处理补全",确保 ClickHouse 表中:

  • 当日数据:实时更新(5 秒窗口),支撑实时大屏展示;
  • 历史数据(T-1 及之前):由批处理结果覆盖,保证数据精准性(如财务对账场景);
  • 差异率控制在 0.5% 以内,满足业务对数据一致性的要求。

4. ADS 层:数据应用封装

ADS 层是 "业务输出端",基于 DWS 层汇总数据,按具体业务需求封装数据,直接支撑前端应用或下游系统调用,核心目标是 "简化查询、提升性能"。

(1)实时大屏场景(MySQL 存储)

业务需求 :电商实时大屏需展示 "商品分类销量 Top10""实时总销售额""近 1 小时销量趋势",要求查询延迟 <100ms。
处理逻辑

  1. 从 ClickHouse 实时表聚合核心指标;

  2. 将结果写入 MySQL(支持高并发读),并创建索引优化查询;

  3. 每 10 秒刷新一次数据,平衡实时性与数据库压力。

SQL 实现(Dinky 平台)

sql 复制代码
-- 1. 读取 DWS 层 ClickHouse 实时汇总表
CREATE TABLE dws_clickhouse_sales_real (
  -- 同前序定义,此处省略
);

-- 2. 创建 ADS 层实时大屏 MySQL 表
CREATE TABLE ads_mysql_sales_dashboard (
  `indicator_name` STRING COMMENT '指标名称(如 total_sales/top10_category)',
  `category` STRING COMMENT '商品分类(top10场景非空,总销量场景为空)',
  `indicator_value` STRING COMMENT '指标值(金额/数量,转字符串便于统一存储)',
  `rank` INT COMMENT '排名(仅top10场景非空)',
  `update_time` TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`indicator_name`, `category`) NOT ENFORCED -- 复合主键避免重复数据
) WITH (
  'connector' = 'jdbc',
  'url' = 'jdbc:mysql://${mysql_servers}/ads_db?useSSL=false',
  'table-name' = 'ads_mysql_sales_dashboard',
  'username' = '${mysql_user}',
  'password' = '${mysql_pwd}',
  'sink.batch-size' = '10',
  'sink.flush-interval' = '10000' -- 10秒刷新一次
);

-- 3. 封装"实时总销售额"指标
INSERT INTO ads_mysql_sales_dashboard
SELECT 
  'total_sales' AS indicator_name,
  '' AS category,
  CAST(SUM(real_sales) AS STRING) AS indicator_value,
  0 AS rank,
  CURRENT_TIMESTAMP() AS update_time
FROM dws_clickhouse_sales_real
WHERE window_end >= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL '1' DAY); -- 统计近24小时总销量

-- 4. 封装"商品分类销量 Top10"指标
INSERT INTO ads_mysql_sales_dashboard
SELECT 
  'top10_category' AS indicator_name,
  category,
  CAST(SUM(real_sales) AS STRING) AS indicator_value,
  ROW_NUMBER() OVER (ORDER BY SUM(real_sales) DESC) AS rank,
  CURRENT_TIMESTAMP() AS update_time
FROM dws_clickhouse_sales_real
WHERE window_end >= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL '1' DAY)
GROUP BY category
QUALIFY ROW_NUMBER() OVER (ORDER BY SUM(real_sales) DESC) <= 10; -- 取销量前10分类

应用调用:实时大屏通过 JDBC 连接 MySQL,执行简单查询即可获取指标:

sql 复制代码
-- 查询实时总销售额
SELECT indicator_value AS total_sales 
FROM ads_mysql_sales_dashboard 
WHERE indicator_name = 'total_sales' 
ORDER BY update_time DESC LIMIT 1;

-- 查询销量 Top10 分类
SELECT category, indicator_value AS sales_amount, rank 
FROM ads_mysql_sales_dashboard 
WHERE indicator_name = 'top10_category' 
ORDER BY rank ASC;

(2)财务报表场景(Hive 存储)

业务需求 :财务部门需生成 "每日商品分类销量报表",包含销量金额、订单数量、客单价(销量金额 / 订单数量),要求数据 100% 精准。
处理逻辑

  1. 基于 DWS 层批处理汇总表(Hive)计算客单价;

  2. 按日期分区存储,支持按 "月 / 季度" 汇总查询;

  3. 数据生成后触发邮件通知,推送报表至财务邮箱。

SQL 实现(Dinky 平台,批模式)

sql 复制代码
-- 1. 切换为批处理模式
SET 'execution.type' = 'batch';

-- 2. 读取 DWS 层批汇总 Hive 表
CREATE TABLE dws_hive_sales_day (
  -- 同前序定义,此处省略
);

-- 3. 创建 ADS 层财务报表 Hive 表
CREATE TABLE ads_hive_finance_sales_report (
  `stat_date` STRING COMMENT '统计日期(yyyy-MM-dd)',
  `category` STRING COMMENT '商品分类',
  `sales_amount` DECIMAL(12,2) COMMENT '销量金额',
  `order_count` BIGINT COMMENT '订单数量',
  `avg_order_price` DECIMAL(10,2) COMMENT '客单价(sales_amount/order_count)',
  `create_time` TIMESTAMP COMMENT '报表生成时间'
) PARTITIONED BY (`stat_date` STRING)
WITH (
  'connector' = 'hive',
  'database-name' = 'ads_db',
  'table-name' = 'ads_hive_finance_sales_report'
);

-- 4. 计算并写入财务报表数据
INSERT OVERWRITE TABLE ads_hive_finance_sales_report PARTITION (stat_date='20240520')
SELECT 
  '20240520' AS stat_date,
  category,
  day_sales AS sales_amount,
  day_order_count AS order_count,
  ROUND(day_sales / day_order_count, 2) AS avg_order_price, -- 计算客单价
  CURRENT_TIMESTAMP() AS create_time
FROM dws_hive_sales_day
WHERE stat_date = '20240520'
  AND day_order_count > 0; -- 避免除数为0

-- 5. 报表推送(通过 Dinky 集成 Shell 任务实现)
-- 执行 Shell 脚本:将 Hive 表数据导出为 Excel,并通过邮件发送给财务部门
5.3.3 数据流转保障机制

为确保数据从 ODS 层到 ADS 层流转过程中的 "准确性、完整性、时效性",平台设计了三大保障机制:

  1. 数据血缘追踪 :通过 Dinky 元数据管理模块,自动记录各层表之间的依赖关系(如 ods_kafka_order_real_timedwd_kafka_order_detaildws_clickhouse_sales_real),支持按 "表 / 字段" 反向溯源,便于故障定位(如 ADS 层数据异常时,可快速定位到 DWD 层清洗逻辑问题);

  2. 数据质量校验:在各层处理节点嵌入质量校验规则,示例如下:

    分层 校验规则 处理方式
    ODS 层 订单 amount >0、order_id 非空 过滤无效数据,记录至错误日志表
    DWD 层 脱敏后手机号格式为 138****5678 格式错误数据暂存至临时表,人工核查
    DWS 层 批流汇总差异率 ≤0.5% 差异超标触发告警,暂停 ADS 层数据更新
    ADS 层 财务报表客单价 ≤ 该分类商品均价的 3 倍 异常数据标红,在报表中注明 "待核查"
  3. 数据备份与恢复

    • 实时数据:Kafka 主题设置 3 副本,避免数据丢失;
    • 批数据:Hive 表开启定时快照(每日凌晨 03:00),支持回滚至前一日状态;
    • 应用数据:MySQL 开启 binlog,支持按时间点恢复(如误操作删除数据后,可恢复至删除前状态)。

5.4 数据架构价值总结

本平台的批流一体数据架构,通过 "分层解耦 + 批流融合" 设计,解决了传统数仓的核心痛点,具体价值如下:

数据一致性:统一批流数据模型与计算逻辑,批处理补全实时数据偏差,结果差异率控制在 0.5% 以内,满足业务对数据精准性的需求(如财务对账、风控决策);

业务适配性:实时链路(毫秒级 - 秒级)支撑实时大屏、库存调整等低延迟场景,批处理链路(T+1)支撑财务报表、月度分析等精准性场景,一套架构覆盖多业务需求;

效率提升:各层数据单向流动,避免重复加工,DWD 层清洗后的数据可复用至 DWS 层多个汇总任务,数据加工效率提升 40%;

可维护性:数据血缘清晰、质量校验自动化、备份机制完善,故障排查时间从传统架构的 2 小时缩短至 15 分钟,降低运维成本。

相关推荐
潘达斯奈基~8 小时前
kafka:【1】概念关系梳理
分布式·kafka
IT果果日记8 小时前
Flink+Dinky实现UDF自定义函数
大数据·后端·flink
字节跳动数据平台8 小时前
火山引擎多模态数据湖落地深势科技,提升科研数据处理效能
大数据
用户Taobaoapi20148 小时前
多店铺数据采集效率低?京东API批量调用接口支持千级商品详情批量拉取
大数据·数据挖掘·数据分析
laoma-cloud9 小时前
Web 集群高可用全方案:Keepalived+LVS (DR) 负载均衡 + Apache 服务 + NFS 共享存储搭建指南
运维·负载均衡·高可用·web集群
子兮曰9 小时前
🔥这个Nginx泛域名配置,让90%的开发者直呼"原来还能这样玩!
运维·前端·nginx
歪歪1009 小时前
如何在Qt Creator中快速定位代码中的注释?
运维·开发语言·qt·解释器模式·全文检索·嵌入式实时数据库
武子康9 小时前
大数据-87 Spark 实现圆周率计算与共同好友分析:Scala 实战案例
大数据·后端·spark
BYSJMG9 小时前
计算机大数据毕业设计选题:基于Spark+hadoop的全球香水市场趋势分析系统
大数据·vue.js·hadoop·python·spark·django·课程设计