2024年,我作为数据架构师参与了某跨境电商平台"实时数据中台"项目的设计与实施,负责实时数仓的架构决策、技术选型与核心链路的治理方案。项目面临的核心矛盾是:原有T+1离线链路无法支撑大促期间的动态定价与风控决策,而直接复制批式分层到流上又会导致状态膨胀与口径漂移。本文论述了以ODS---DWD---DWS---ADS为主线的实时分层架构,阐明"可回放优先于层数完备"的分层判据,并给出流批一体在口径统一与存储共用两个维度上的落地路径。针对数据接入、数据质量、端到端延迟与流数据状态管理四类典型问题,分别采取了CDC两阶段入湖、双链路口径对账、Watermark与反压协同调优、RocksDB状态后端与增量Checkpoint等对策。上线后端到端P99延迟稳定在8秒以内,大促峰值吞吐达到12万条/秒,实时与离线核心指标日终对账偏差收敛至0.3%以下。
一、项目背景与主要工作
该跨境电商平台日均订单量约300万,原有数据体系以Hive离线数仓为主,报表产出时间为T+1凌晨。2024年"黑五"大促前,业务方提出两项刚性需求:动态定价引擎需要在秒级感知各站点的库存与竞品价格变化;风控团队要求异常订单识别延迟不超过10秒。传统Lambda架构下,实时链路用Flink做简单聚合、离线链路用Spark做全量修正,两套代码两套口径,大促期间曾出现实时GMV与离线GMV相差7%的事故。项目目标是构建一套以流批一体为原则、以可回放为底线的实时数仓,替代原有的割裂架构。
我在项目中担任架构师,主要工作包括三项:第一,确定分层架构中各层的职责边界与物化时机,避免实时链路照搬批式分层导致状态不可控;第二,完成计算引擎与消息中间件的选型论证,确立以Flink为流处理核心、Kafka为ODS缓冲的技术栈;第三,牵头解决数据接入的一致性、实时数据的质量校验、端到端延迟的稳定性以及有状态算子的状态规模控制等落地难题。
二、实时数仓分层架构与流批一体设计
2.1 ODS/DWD/DWS/ADS的实时化取舍
实时数仓的分层不能把批式四层原样搬到流上,核心判据不是"层数是否齐备",而是可回放性与口径一致性。
ODS层 的首要职责不是"贴源存储",而是可回放。原始CDC binlog与业务事件写入Kafka并落湖保留,下游任何一层口径变更或逻辑缺陷,都能从ODS重放修复,而不必回查源库。对于核心订单表与库存表,采用长期保留策略;低价值高频埋点事件按保留期滚动清理。
DWD层 在流内完成影响口径的浅层清洗:去空、类型统一、脏数据过滤、时间戳标准化,以及必要的维度退化。不做展示型加工,也不在DWD层做大范围维表关联------复杂关联留给有外部查找能力的组件或由DWS按需完成。这一取舍直接控制了DWD的状态规模。
DWS层 只做高频且口径稳定的轻度预聚合。实时场景下状态成本远高于批式,预聚合粒度过细会导致状态随指标数量线性膨胀。项目将DWS限定为"当日累计GMV""近5分钟订单量""分站点库存水位"等不超过15个核心指标,长尾分析一律通过DWD明细加OLAP引擎按需聚合兜底。
ADS层 的物化时机由下游SLA分级决定,而非由计算引擎统一驱动:秒级看板与风控决策走事件驱动写入HBase/Redis;分钟级运营报表走滚动窗口聚合;日级结算与对账走定时批处理,实时链路只保证明细完整,汇总交给批任务。
2.2 流批一体的两个落点
流批一体在本项目中不是一个口号,而是两个可验证的工程约束。
口径统一是第一个落点。实时指标与离线指标必须来自同一份指标定义,而非两套SQL各自维护。项目建立了统一的指标口径层,DWS与ADS的指标逻辑通过参数化的Flink SQL模板表达,同一模板在流模式下以增量方式运行、在批模式下以有界数据全量运行,从源头上消除"实时一个数、离线另一个数"的割裂。
存储共用是第二个落点。ODS与DWD尽量落在同一湖仓存储上,Flink实时写入与批式回补任务读取的是同一份物理数据,避免双链路之间反复对账与数据搬迁。这一设计使得"回补"不再是独立工程,而只是以批模式重新执行同一套SQL。
2.3 实时计算引擎选型要点
引擎选型围绕三个不可妥协的约束展开:状态管理能力、端到端一致性保障、反压可控性。
Flink在三个维度上均满足要求:原生Keyed State与RocksDB状态后端支撑大状态下的有状态流处理;Barrier驱动的Checkpoint机制配合两阶段提交Sink可实现端到端Exactly-Once;基于信用值的反压机制使链路拥塞可观测、可定位。Kafka作为ODS缓冲层,其分区模型与Flink Source并行度直接对齐,且具备可回放的消息保留能力,与实时数仓"ODS可回放"的底线要求天然匹配。未选择纯Kappa架构的原因在于:部分日级结算场景仍需批式全量修正,而流批一体的SQL模板已经覆盖了这一需求,无需额外维护一条纯实时链路。
三、关键问题与对策
3.1 数据接入:两阶段CDC与幂等写入
数据接入面临两个约束:不能对源业务库造成显著压力,同时要保证变更事件不丢不重。项目采用两阶段CDC入湖机制:第一阶段从MySQL从库以低频率快照方式完成存量数据的初始同步;第二阶段切换为binlog增量捕获,增量事件先写入Kafka ODS层,再由Flink消费写入湖仓存储。Flink Sink启用两阶段提交,结合Kafka offset与Checkpoint的绑定,实现"接入---处理---落湖"的端到端Exactly-Once。对于维度表变更等非严格有序场景,在Sink端增加主键去重与版本比较作为兜底,避免乱序导致的维度过期。
3.2 数据质量:口径对账与流内校验双轨并行
实时数据的质量不能依赖事后离线对账来发现,但完全依靠流内规则又无法覆盖口径层面的系统性偏差。项目采取双轨策略。
在流内,DWD层嵌入轻量校验规则:字段长度与枚举值合法性检查、主键唯一性检查、事件时间与到达时间的偏离阈值告警。校验不通过的数据进入旁路侧输出,不阻塞主链路,但触发分钟级告警。
在口径层面 ,每日凌晨以ODS回放数据为基准,分别运行实时链路的SQL模板和批式全量模板,对核心指标做逐项对账。偏差超过0.5%即冻结对应ADS表的对外服务,定位到具体算子后从ODS回补。这一机制将"流批一体"从架构设计延伸到了运维契约:口径一致不是假设,而是每日被验证的事实。
3.3 数据延迟:Watermark、反压与物化时机的协同
端到端延迟由三段时间构成:源端滞后、计算处理时间、结果写出时间。项目没有追求单一环节的极限优化,而是在三个环节之间做匹配。
源端 通过Kafka分区与Flink Source并行度对齐,消除消费端的结构性倾斜。计算端 以Event Time + Watermark处理乱序,Watermark延迟设为5秒,在乱序容忍与触发时效之间取平衡;同时开启反压监控,当某算子反压持续超过阈值时,优先拆分该算子的KeyBy热点或降低窗口聚合粒度,而非盲目增加全局并行度。物化端严格执行ADS按SLA分级:秒级指标事件驱动物化,分钟级指标窗口聚合,日级指标交给批任务。如果秒级指标也统一走事件驱动,写放大会反向推高计算层延迟,分级物化本身就是延迟控制手段。上线后P99端到端延迟稳定在8秒以内,大促峰值吞吐达到12万条/秒。
3.4 流数据状态管理:RocksDB + 增量Checkpoint + 状态TTL
有状态算子的状态规模是实时链路最隐蔽的风险。DWD的维表关联缓存、DWS的窗口聚合状态如果放在JVM堆内存中,一次大促流量脉冲就可能触发OOM。项目统一采用RocksDB状态后端,将状态溢出到本地磁盘,使状态容量不再受堆内存硬约束。Checkpoint间隔设为60秒,启用增量Checkpoint以降低每次持久化的I/O开销;Checkpoint目录放在HDFS上,保证TaskManager故障时状态可恢复。
在此基础上,为每一类状态显式设置TTL:DWD维表缓存TTL与维表更新频率对齐,DWS窗口状态TTL为窗口长度加允许迟到时间,过期状态由Flink自动清理。TTL的设置原则是状态只保留"口径正确性所需的最短历史" ,而非尽可能长。这一原则将状态成本从"随运行时间线性增长"转变为"随指标数量与窗口长度有界增长"。
四、总结
该实时数仓上线后支撑了动态定价、实时风控与运营大屏三类核心场景,端到端P99延迟稳定在8秒以内,实时与离线核心指标日终对账偏差收敛至0.3%以下。回顾架构决策,有两点体会:分层架构的价值不在层数,而在每一层是否承担了明确的、可验证的取舍 ------ODS的可回放性决定了纠错成本的上限,DWS的受限预聚合决定了状态成本的上限,ADS的SLA分级物化决定了延迟与写放大之间的平衡点。流批一体的真正难点不在存储统一,而在口径统一;没有每日对账作为运维契约,"流批一体"很容易退化为两套数字的技术修辞。实时数仓的架构设计,归根到底是把"实时"从一种计算模式,转化为一组可度量、可回退、可对账的工程约束。