1. DWS层概述:实时数仓分层架构、DWS层定位与职责

大家好,我是老张。做实时数仓这些年,我见过太多团队一上来就怼 Flink SQL,结果写到后面发现指标对不上、数据乱成一锅粥。说白了,问题出在分层没做好。今天咱们就聊聊 DWS 层——这个实时数仓里最容易被低估的角色。

1.1 实时数仓分层架构

先看一张我手绘的架构图。这张图在我电脑里迭代了不下 20 个版本,算是比较成熟的分层方案了。

实时数仓分层架构图 ODS 源数据层 Kafka Topic / Canal Binlog / 日志采集 DWD 明细数据层 数据清洗 · 去重 · 维度补齐 · 事件标准化 DIM 维度层 用户 · 商品 · 门店 · 日历 DWS 汇总层 实时聚合 · 预计算 · 宽表构建 ADS 应用数据层 大屏 · 报表 · 实时预警 · 数据服务 API 💡 关键点: DWS 层是实时数仓的“承重墙”——既要承接 DWD 的明细数据,又要为 ADS 提供秒级可用的聚合结果。

这张图我用了好几年,每次给新团队培训都会拿出来讲。你仔细看,DWS 层正好卡在中间——上面接着 DWD 的明细数据,下面喂给 ADS 做应用。这个位置决定了它的核心职责:预聚合。

1.2 DWS层的定位与职责

DWS 的全称是 Data Warehouse Summary,汇总层。说白了,就是把 DWD 层那些细碎的明细数据,按照业务维度提前算好。为什么要提前算?我举个例子你就明白了。

假设你们要做实时大屏,要展示「今日全站 GMV」。如果每次查询都去扫 DWD 层的订单明细表,几亿条数据,Flink 再快也扛不住。DWS 层做的就是:每来一条订单,就实时累加到对应的维度桶里。查询的时候直接读结果,毫秒级响应。

我个人习惯:DWS 层的聚合粒度,通常按「业务域 + 维度组合 + 时间窗口」来定。比如:

  • 交易域:店铺 + 商品 + 1分钟
  • 流量域:页面 + 来源 + 5分钟
  • 用户域:新老客 + 渠道 + 1小时

粒度太细,数据量还是大;粒度太粗,下游用起来不方便。这个度,得靠经验来把握。

DWS 层的具体职责,我归纳为三点:

  1. 实时聚合计算:对 DWD 层流入的明细数据,按预定义的维度组合做 sum、count、avg、distinct count 等聚合操作。
  2. 预构建宽表:把高频查询需要的维度字段和度量字段拼成一张宽表,减少下游的 join 操作。
  3. 数据降维:把海量明细数据压缩成可管理的汇总数据,降低存储成本和查询延迟。

⚠️ 我曾经踩过一个坑:刚开始做实时数仓时,我把所有指标都放在 DWS 层做全量聚合。结果状态后端撑爆了,Checkpoint 一直失败。后来才明白——DWS 层不是万能的,高频维度的聚合放 DWS,低频维度的查询直接走 DWD 层更划算。

1.3 DWS与DWD/DIM/ADS的关系

很多新手搞不清这几层到底怎么配合。我画个表格,你一看就懂。

层级 核心产出 与DWS的关系 典型存储
DWD 清洗后的明细事件 DWS 的输入源,消费 DWD 的 Kafka 流 Kafka / HDFS
DIM 维度字典数据 DWS 聚合时做维度关联,补齐维度属性 Redis / HBase / MySQL
DWS 预聚合指标宽表 自身,承上启下 ClickHouse / Druid / Kafka
ADS 应用层指标结果 DWS 的输出目标,ADS 直接查询 DWS 的聚合表 MySQL / Redis / API

你看这个关系,其实就是一个流水线:

  • DWD → DWS:Flink 消费 DWD 层的 Kafka 流,按维度 key 做 group by,实时聚合。
  • DIM → DWS:聚合过程中,通过异步 IO 或者广播流,把维度信息补齐。比如订单流里只有 user_id,DWS 层要关联出用户的城市、等级、新老客标签。
  • DWS → ADS:聚合结果写入 OLAP 引擎或者消息队列,ADS 层直接读取,做最后的格式化和展示。

这里有个设计原则:DWS 层尽量保持「幂等」。什么意思?就是同样的数据重跑一遍,结果必须一样。我见过太多团队因为 DWS 层的聚合逻辑没处理好,导致数据对不上,排查起来痛不欲生。我的建议是——DWS 层的聚合算子,一定要用 Flink 的精确一次语义,状态后端用 RocksDB,Checkpoint 间隔别超过 30 秒。

1.4 一个真实案例

去年我帮一家电商公司搭实时数仓,他们的场景是这样的:

  • DWD 层:每秒涌入 5 万条订单事件,存储在 Kafka
  • 需求:实时计算「每个店铺 + 每个商品 + 每分钟」的 GMV、订单量、客单价
  • ADS 层:大屏每 5 秒刷新一次,要求延迟不超过 10 秒

如果直接拿 DWD 层的数据做大屏查询,ClickHouse 再快也扛不住每秒几万次的扫描。我们的方案就是:在 DWS 层用 Flink 做 1 分钟的滚动窗口聚合,结果写入 ClickHouse 的 AggregatingMergeTree 表。大屏查询时,直接读 DWS 层的预聚合结果,查询耗时从原来的 3 秒降到了 50 毫秒。

嗯,这就是 DWS 层的价值——用空间换时间,用预计算换查询速度。