数据仓库教程

数仓来源

为什么要数仓

数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。

是为了统计,决策!

数仓中表是面向主题的

  • 后端思维 (面向应用):

    你在设计 MySQL 表时,通常是按功能模块设计的。

    • 例子: user_service 库里有用户表,order_service 库里有订单表,log_service 库里有日志表。这些表是为了配合你的 Java微服务 运行的。
  • 数仓思维 (面向主题):

    数据仓库不在乎你是哪个微服务产生的,它只在乎分析对象。

    • 例子: 数仓会建立一个 "客户 (Customer)" 主题。它会把 user_service 里的注册信息、order_service 里的收货地址、log_service 里的最近登录时间,全部清洗后放在一起,形成一个全方位的"客户画像"。
    • 目的: 提供对分析对象的一个完整、一致的描述。

数仓中数据来源

数据进入数据仓库之前,必然要经过统一与综合。

这是后端工程师最容易理解的痛点。

  • 现状 (后端乱象):

    • 系统 A (Java): 性别字段用 0/1 (int)。
    • 系统 B (Python): 性别字段用 'M'/'F' (char)。
    • 系统 C (老系统): 日期格式是 2023/11/22
    • 系统 D (新系统): 日期格式是 1700640000 (时间戳)。
  • 数仓规范 (集成):

    数据仓库就像一个"强迫症的收纳师"。不管你源头是什么样,进到数仓必须统一:

    • 性别统一为 Male/Female
    • 日期统一为 yyyy-MM-dd
    • 核心价值: 解决了同名异义、异名同义、单位不统一等矛盾。只有这样,分析师才能放心地写 SQL 聚合。

架构分层

1. ODS 层

  • 英文全称: Operational Data Store
  • 中文名称: 数据运营层
  • 含义: 最接近数据源的一层,存放接入的原始数据。

2. DW 层 (总称)

  • 英文全称: Data Warehouse
  • 中文名称: 数据仓库层
  • 含义: 核心设计层,通常包含 DWD, DWM, DWS 三个子层。

2.1 DWD 层

  • 英文全称: Data Warehouse Detail
  • 中文名称: 数据明细层
  • 含义: 保持和 ODS 一样的数据粒度,但进行清洗和质量保证。

2.2 DWS 层

  • 英文全称: Data Warehouse Service (注:原文拼写为 Servce ,标准应为 Service)
  • 中文名称: 数据服务层
  • 含义: 又称数据集市或宽表,按照业务划分(如流量、订单),生成字段较多的宽表。

4. APP 层

  • 英文全称: Application
  • 中文名称: 数据应用层
  • 含义: 完全为了满足具体的分析需求而构建的数据,通常是高度汇总的数据。

实例

你好!我是"大数据领航员"。

这部分是数仓设计的核心 。很多后端转大数据的同学最容易晕的就是:"为什么不直接从 ODS 算结果?非要搞个 DWD、DWS 这么麻烦?"

我们通过一个具体的**"外卖订单"**场景,带着一条数据走完这三层,你就明白每一层的存在意义了。


场景设定:

业务动作: 用户"张三"在"2023-11-22 12:30"点了一份"隆江猪脚饭",支付了 25 元。

1. ODS 层

关键词: 原封不动、备份。

  • 数据长什么样?

    你的业务数据库(MySQL)里的 order_info 表是什么样,这里就是什么样。可能连字段名里的拼写错误都一模一样。

    复制代码
    // ODS 层的一行数据 (几乎就是 MySQL Binlog 的转储)
    {
      "id": "10086",
      "uid": "u_9527",        // 关联用户ID
      "shop_id": "s_888",     // 关联店铺ID
      "pay_amt": 2500,        // 这里的单位是"分" (后端常见设计)
      "status": 1,            // 1 代表已支付 (魔法数字)
      "ctime": 1700627400000, // 时间戳
      "ext": "{\"spicy\": 1}" // 扩展字段 JSON
    }
  • 后端视角的价值:

    "后悔药"。 如果你后面的代码写错了,或者想换一种分析逻辑,原始数据还在,随时可以重跑。

2. DW 层

这一层要把"不可读"的数据变成"好用"的数据。我们拆开看:

2.1 DWD 层

关键词: 去脏、统一格式、反魔法值。

  • 做什么?

    • 清洗: 比如发现有一条数据的 pay_amt 是负数(脏数据),这一层直接过滤掉。
    • 转换: 把"分"转成"元";把时间戳转成 yyyy-MM-dd
    • 维度退化: 这是一个高级词。比如 ext 里的 spicy: 1,在 ODS 里是 JSON 字符串,很难查。在 DWD 层,我们会把它提取出来,变成一个独立的字段 is_spicy
  • 数据长什么样?

    复制代码
    -- DWD 层的数据 (变得像人看的了)
    order_id: "10086"
    user_id: "u_9527"
    shop_id: "s_888"
    pay_amount: 25.00       -- 变成了元
    order_status: "PAID"    -- 翻译了状态码 1 -> PAID
    create_time: "2023-11-22 12:30:00" -- 变成了时间字符串
    is_spicy: 1             -- 从 JSON 里提出来的
    dt: "2023-11-22"        -- 分区字段
2.2 DWS 层

关键词: 空间换时间、反范式、宽表。

  • 做什么?

    这是后端最不习惯的一层。在 MySQL 里,你想知道"张三是哪里人",你会 Join user_table。但在大数据里,几亿行 Join 是灾难。

    所以,我们在这一层提前把 Join 做好。把用户表、店铺表的信息,全部"甚至冗余"地拼到订单表上来。

  • 数据长什么样? (注意看,这行数据变得巨宽无比)

    复制代码
    -- DWS 层宽表 (dws_trade_order_detail_day)
    -- 核心订单信息
    order_id: "10086"
    pay_amount: 25.00
    
    -- 拼进来的【用户】信息 (冗余存储)
    user_id: "u_9527"
    user_name: "张三"      -- 以后查名字不用 Join 了
    user_gender: "男"
    user_city: "北京"
    
    -- 拼进来的【店铺】信息 (冗余存储)
    shop_id: "s_888"
    shop_name: "隆江猪脚饭(西二旗店)"
    shop_category: "快餐"
    
    dt: "2023-11-22"
  • 后端视角的价值:

    "自助餐台"。 分析师想看"北京男用户的消费总额"?直接查这张表 WHERE user_city='北京' AND user_gender='男' 即可,完全不需要 Join。效率提升 100 倍。

APP 层

关键词: 高度汇总、结果集。

  • 做什么?

    老板要看大屏:"全国各城市的实时销售额排名"。你不能每次都去 DWS 层扫几亿行数据算 SUM。

    所以,我们提前算出结果,存在 APP 层(通常同步回 MySQL 或 Redis)。

  • 数据长什么样?

    复制代码
    -- APP 层 (app_city_sales_daily)
    city_name: "北京"
    total_gmv: 1000000.00
    order_cnt: 50000
    dt: "2023-11-22"
  • 后端视角的价值:

    "API 接口数据源"。 你的 Java 代码写的接口:

    public SalesVO getCitySales(String date)

    里面执行的 SQL 就是:SELECT * FROM app_city_sales_daily WHERE dt = ?。

    响应时间:10ms。

总结

层级 就像做菜... 如果没有这一层...
ODS 刚买回来的带泥土豆 数据错了没法回溯,只能去 MySQL 生产库查,会被运维打死。
DWD 洗净削皮切好的土豆丝 每个人都要自己写代码去解析"分转元"、去解析 JSON,重复造轮子。
DWS 半成品预制菜(土豆肉丝) 每次查报表都要把几亿张订单和用户表 Join 一次,集群资源瞬间耗尽。
APP 端上桌的成品菜 报表加载速度极慢,老板点开页面要等 5 分钟。

维度建模

事实表和维度表

维度表是名词(实体/性质),事实表是动词(动作/事件)。

这个例子选得非常好。我们用 "2023-11-11 购买 iPhone 15" 这个具体的业务场景,把抽象的概念直接落地成数据库里的 "行"和"列"

在数据仓库(特别是星型模型)中,这条数据会被拆解并分别存储在 维度表事实表 中。

1. 维度表 (Dimension Table):存储"环境描述"

核心作用: 它是你的查询字典。它告诉我们"ID 是 P_1001 的商品到底长什么样"。

维度表存储的是业务实体的属性信息 (文本描述为主)。对于 iPhone 15,我们会存入一张商品维度表(比如 dim_product)。

表结构模拟:

product_id (主键) product_name (名称) brand_name (品牌) category (品类) color (颜色) launch_date (上市时间)
P_1001 iPhone 15 Apple 手机 黑色 2023-09
  • 存储内容: 商品的静态描述信息。注意,这里不存你今天卖了多少钱,只存"iPhone 15 是 Apple 生产的黑色手机"这个事实。
  • 数据特点: 相比事实表,数据量小(商品 SKU 有限),变化慢。
  • 用途: 用来做 SQL 中的 过滤 (WHERE)分组 (GROUP BY)
    • 例子: 当你想查"Apple 品牌 的销量"时,数仓会先来这张表找到 brand_name = 'Apple' 对应的 product_id

2. 事实表 (Fact Table):存储"动作与度量"

核心作用: 它是你的流水账本。它记录"发生了什么事"以及"数值是多少"。

事实表存储的是业务过程 (下单)产生的数据度量 。对于这笔交易,我们会存入一张订单事实表(比如 dwd_fact_order)。

表结构模拟:

order_id (主键) user_id (谁买的) product_id (买了啥) date_id (何时买) payment_amount (金额) quantity (数量)
O_20231111_01 U_888 P_1001 20231111 8999.00 1
  • 存储内容:
    1. 外键 ID: user_id, product_id, date_id。注意,这里不存 "iPhone 15"这个字符串,只存 ID P_1001。这能极大节省存储空间。
    2. 度量值 (Metrics): 8999.00 (金额), 1 (数量)。这是可以用来做加减乘除数学运算的。
  • 数据特点: 数据量巨大,每天新增千万条记录。
  • 用途: 用来做 SQL 中的 聚合计算 (SUM, COUNT, AVG)
    • 例子: 最终计算"销售额"时,是把这张表里的 payment_amount 加起来。

3. 它们如何配合工作?

当你作为后端工程师,想在数仓里执行这个查询:

"统计 2023-11-11 当天,Apple 品牌的销售总额。"

数仓的执行逻辑是这样的:

  1. 查维度表 (dim_product):
    • 找到 brand_name = 'Apple' 的记录。
    • 提取出对应的 ID:P_1001 (iPhone 15), P_1002 (iPhone 14)...
  2. 查事实表 (dwd_fact_order):
    • 带着上面找到的 ID (P_1001 等) 和时间条件 (20231111) 去流水账里筛选。
    • 找到所有匹配的行。
  3. 聚合计算:
    • 把筛选出来的行里的 payment_amount (8999, 7999...) 全部 SUM 起来。

总结

  • 维度表 存的是:iPhone 15, Apple, 黑色 (描述信息,用于筛选)。
  • 事实表 存的是:P_1001, 8999, 1 (ID 和数字,用于计算)。

这种**"事实表 + 维度表"的设计模式,就是文档中提到的"星型模型"**,它是数仓中效率最高、最常用的建模方式。

星型模型

注意:事实表大部分都是ID或者数字

场景: 我们要分析 "瑞幸咖啡""北京市" 的销售总额。

1. 以前你的做法(后端 MySQL 思维)

你会把数据拆得非常散,为了不冗余(雪花模型):

  • Order表:user_id
  • User表:city_id
  • City表:province_id
  • Province表:province_name ("北京市")。

❌ 查询痛苦: 你需要 JOIN 4 张表 才能把"订单"和"北京市"关联起来。

2. 星型模型的做法(数仓 Hive 思维)

我们在数仓里只建 1 张中心事实表1 张扁平的用户维度表

⭐ A. 核心:事实表 (fact_order)

只存 ID 和 钱。这是星星的"核心"。

order_id user_id (FK) product_id (FK) amount (度量)
1001 U_001 P_Latte 25.00
1002 U_002 P_Americano 15.00
1003 U_001 P_Latte 25.00
  • 注:这里看不出是哪个城市的,只有 ID。
⭐ B. 射线:维度表 (dim_user)

把省份、城市全部"压扁"放进来。这是星星的"光芒"。

user_id (PK) user_name city_name province_name
U_001 张三 朝阳区 北京市
U_002 李四 浦东新区 上海市
U_003 王五 海淀区 北京市
  • 注:你看,"北京市" 重复出现了(数据冗余),但没关系,我们要的是方便。

3. 实际中怎么用?(SQL 演示)

现在老板问:"北京市卖了多少钱?"

后端 MySQL 写法 (痛苦):

sql 复制代码
SELECT sum(o.amount)
FROM Order o
JOIN User u ON o.user_id = u.id
JOIN City c ON u.city_id = c.id      -- 多一次 Join
JOIN Province p ON c.prov_id = p.id  -- 又多一次 Join,性能爆炸
WHERE p.name = '北京市';

数仓 星型模型 写法 (爽快):

sql 复制代码
SELECT 
    sum(f.amount) 
FROM 
    fact_order f         -- 事实表
JOIN 
    dim_user u           -- 维度表
    ON f.user_id = u.user_id
WHERE 
    u.province_name = '北京市'; -- 直接在维表里过滤

总结

在这个例子中:

  1. 事实表负责记录"收了 25 块钱"。
  2. 维度表负责记录"U_001 是北京市的"。
  3. 星型模型 就是让你只 Join 一次 就能算出结果,用存储空间 (User表里重复存北京)换取了查询时间(少 Join 两张表)。

ETL与调度

ETL 代表的是数据在进入数据仓库之前必须经历的三个阶段:

阶段 英文全称 中文含义 核心工作内容 后端视角类比
E Extract 抽取 从各种数据源(如你的 MySQL 数据库、应用日志、第三方 API)中读取数据。 远程调用 DAO 层,执行 SELECT 命令,或监听 Binlog
T Transform 转换/清洗 这是 ETL 的核心,对原始数据进行清洗、转换和整合。 编写 Service 层的业务逻辑,对数据进行格式校验、去重、去噪、单位统一(如把 0/1 转换为 Yes/No)、数据计算与聚合。
L Load 加载 将经过清洗和转换后的数据,装入目标数据仓库(如 Hive 的某个分区)。 调用 Mapper/Writer 将处理好的数据批量 INSERT OVERWRITE 到下游表。

好的!我是"大数据领航员"。我将为您精炼和重构这部分知识,使之成为逻辑清晰、重点突出的面试标准答案。

在面试中,对于 ETL 和调度的问题,您需要体现出**"后端思维"与"大数据思维"**的差异,并强调 "效率"与"依赖" 这两个核心词。

面试精讲:ETL 与调度

这部分回答的核心思路是:先说方法(ETL),再说保证(调度)。

一、ETL 数据搬运策略 (E & L 阶段)

ETL 即 Extract(抽取)、Transform(转换)、Load(加载)。在数据抽取和加载(E & L)阶段,核心问题是:如何高效、低成本地将数据从业务库导入数仓?

1. 全量同步 (Full Load)
  • 概念: 每次抽取时,将源表中的全部数据进行导出和覆盖式加载。
  • 适用场景: 数据量小、变动频率不高的维度表(如地区、配置字典)。
  • 优点: 逻辑简单,数据一致性有保证(每天都是最新快照)。
  • 对比: 类似于后端服务启动时全量加载配置到缓存
  • 面试加分点: 需警惕全量同步对业务数据库的压力。
2. 增量同步 (Incremental Load)
  • 概念: 仅抽取源表中在上一调度周期内新增或发生变化的数据。
  • 适用场景: 数据量巨大的交易流水(订单、支付)等事实表。
  • 优点: 抽取速度快,对业务库(OLTP)压力小,符合大数据**"只追加、不修改"**的原则。
  • 对比: 类似于后端通过 create_timeupdate_time 字段进行条件查询。
  • 面试加分点: 难点在于如何处理 UPDATEDELETE 操作,通常需要通过监听 Binlog 或使用拉链表技术来维护历史快照。

二、调度系统与依赖管理 (核心 T 阶段保障)

大数据调度的核心挑战不是"时间",而是如何确保上下游数据之间的依赖关系

1. 为什么不能依赖时间 (Cron)?
  • 痛点: 传统的定时任务基于时间执行,但在大数据场景下,上游 ETL 任务的完成时间不稳定(受数据量、集群资源影响)。
  • 后果: 如果下游任务在数据未完全导入时启动,会导致数据计算结果错误(如计算出大量 0),产生数据错乱和质量问题
2.核心机制:基于依赖的 DAG (有向无环图)
  • 作用: 调度系统的核心是构建 DAG,确保任务只有在所有上游依赖任务成功完成后才会被触发执行。
  • 流程体现: 确保数据流严格按照 ODS(原材料就绪)-> DWD -> DWS -> APP(成品输出) 的顺序流动。
  • 元数据的作用: 调度系统依赖元数据来记录和查询任务状态,以及定义数据抽取的时间安排。
3. 调度工具与对比
  • Airflow: 全球主流,基于 Python 代码定义任务(DAG),代码化、灵活性高。
  • DolphinScheduler(海豚调度): 国产主流,基于 Java 开发,界面可视化操作,易于非数据工程师上手。
4. 实战例子(加分项)

场景: 计算 DWS 层日报表。

依赖链: 任务 A (ODS同步) → \rightarrow → 任务 B (DWD清洗) → \rightarrow → 任务 C (DWS汇总)

调度逻辑: 任务 B 不在固定时间(如 03:00 AM)执行,而是监控任务 A 的状态。一旦任务 A 在 02:30 AM 成功完成,任务 B 立即启动,保证数据新鲜且准确

面试总结话术

"我的理解是:ETL 是数仓的生命线。维度表(配置)我选择全量同步;事实表(流水)我选择增量同步 ,以降低业务库压力。在调度上,我不会使用简单的 Cron 任务,而是采用 Airflow 或 DolphinScheduler ,通过 DAG 依赖管理来严格控制数据链路,确保上游数据就绪后再触发下游计算,从而根本上解决数据错乱和质量问题。"

相关推荐
会编程的李较瘦1 小时前
Spark SQL 窗口函数全面解析:概念、语法与实战案例
大数据·spark
武子康1 小时前
大数据-174 Elasticsearch 查询 DSL 实战:match/match_phrase/query_string/multi_match 全解析
大数据·后端·elasticsearch
源码技术栈1 小时前
Java智能诊所管理系统源码 SaaS云门诊运维平台源码
java·大数据·运维·人工智能·源码·诊所·门诊
金融小师妹2 小时前
机器学习驱动分析:ADP就业数据异常波动,AI模型预测12月降息概率达89%
大数据·人工智能·深度学习·编辑器·1024程序员节
智慧化智能化数字化方案2 小时前
ERP规划——解读86页大型企业业务流程优化及ERP整体规划方案【附全文阅读】
大数据·人工智能·erp整体规划方案·erp实施项目建设方案·erp基本概念培训
ii_best2 小时前
用鹰眼投屏软件注册Vinted,跨境入门效率翻倍教程
大数据·运维·服务器
士心凡2 小时前
Spark
大数据·ajax·spark
jiayong232 小时前
Elasticsearch 核心概念详解:Index、Document、Field
大数据·elasticsearch·jenkins
AIsdhuang3 小时前
2025 AI培训权威推荐榜:深度评测与趋势前瞻
大数据·人工智能·python