简介:这是一份面向大数据与数仓岗位求职者的实时数仓面试题汇总,内容精炼,覆盖面试高频考点,适合面试前突击复习,也可作为系统梳理数仓知识体系的参考。全包仅含1个PDF文件,大小约89KB,轻量便携,可随时在手机或电脑上阅读。正文按问答形式展开,涵盖数仓理论、MapReduce、Hive、SQL、Kafka等核心模块:既辨析星型模型与雪花模型的优缺点、说明数仓分层结构,也详解MapReduce全流程、FileInputFormat切分算法、HDFS写入流程;针对Hive常见难题,给出了数据倾斜和小文件问题的处理方案,并梳理了HQL转MapReduce的执行思路。SQL部分介绍了grouping sets、cube、rollup的聚合差异,以及Kafka中offset管理与exactly once实现方式;开放题部分还整理了数据异常排查、数据质量保障、调度任务交接等实战经验。目前已有623人学习,对快速补齐实时数仓高频面试考点很有帮助。
1. 2021数仓面试题汇总把考点藏在哪:分层、建模与离线技术栈
考数据仓库岗位,面试官手里那套题翻来覆去就那么几板斧。2021数仓面试题汇总.pdf 看似只是当年面经的堆叠,实际暴露了三个固定考点带:第一是数仓分层,问“你们 ODS 到 ADS 怎么分、数据流向是什么”;第二是数仓建模,问事实表和维度表的区别、缓慢变化维度怎么处理;第三是离线技术栈,Hive、Spark、Kafka 随机挑一个问原理。这三个考点覆盖了初级和中级数仓开发 80% 以上的面试时长。这篇文章按这条主线拆解,每一块都给出可复用的答题结构和被追问时的应变办法,用一份 2021 年的 PDF,讲透 2026 年还能用的答题思路。
2. 数仓分层及各层作用:从 ODS 到 ADS 的职责边界与高频追问
2.1 数仓分层的经典四层结构:每一层只做一件事
数仓分层的核心目的不是把表堆得好看,而是要管理数据加工过程中的血缘和复用。最经典的数仓分层是 ODS、DWD、DWS、ADS 四层,少数公司会拆出 DIM 层,或把 DWD 细分成明细层与轻度汇总层。2021数仓面试题里“数仓分层及各层作用”基本是必考题,直接背概念得分不高,要把每层之间的责任边界说清楚。
| 层级 | 核心职责 | 表命名习惯 | 数据粒度 |
|---|---|---|---|
| ODS | 原样接入业务库、日志、消息队列的数据,不丢字段 | ods_表名_inc / _full | 与源系统一致 |
| DWD | 清洗、去重、统一格式,生成明细事实表 | dwd_主题_明细_inc | 最细粒度业务事件 |
| DWS | 按维度做轻度汇总,面向复用 | dws_维度_主题_di | 用户/商品等业务对象粒度 |
| ADS | 面向应用或报表的个性化加工 | ads_应用名_指标_di | 指标口径可定制 |
注意:考试时不必死背每一层英文全称,但必须能讲清“上一层的输出是什么、下一层拿到后做了什么”。面试官最喜欢按这条线追问“ODS 和业务库到底有什么区别”,答案要落在“ODS 只保留原始事实,不做业务过滤”上。
2.1.1 以用户订单为例看数据怎么一层层流动
假设业务端有订单明细表 order_detail,从 ODS 到 DWD 的加工路径是这样的:
-- ODS 层:原样接入,只做基础校验,不要做业务过滤 CREATE TABLE IF NOT EXISTS ods.ods_order_detail_inc ( order_id STRING, user_id STRING, sku_id STRING, sku_num BIGINT, create_time STRING ) PARTITIONED BY (dt STRING) STORED AS ORC; -- DWD 层:清洗空时间戳、转类型,生成最细粒度的事实明细 INSERT OVERWRITE TABLE dwd.dwd_order_detail_inc PARTITION (dt='${bizdate}') SELECT order_id, user_id, sku_id, sku_num, CAST(create_time AS TIMESTAMP) AS create_time FROM ods.ods_order_detail_inc WHERE dt = '${bizdate}' AND create_time IS NOT NULL AND sku_num > 0;ODS 层只做两件事:按天分区存储、基础类型透传,不处理业务语义上的脏数据。DWD 层才承担清洗任务,把空时间戳过滤掉、负数数量排除、字符串时间转成时间戳。这种分工是“分层边界”题的标准答法。新人容易犯的错是让 ODS 直接过滤,一旦上游口径变化需要重放历史数据,ODS 里已经没有原始字段可用,血缘断掉后就只能返工。
2.2 数仓分层对应的 SQL 面试题:重算与回刷怎么设计
分层结构搭好后,面试题会落到实际操作上,最常见的是“某一天的数据算错了,怎么回刷”。这道题考的是对分层的依赖方向理解:ODS 重拉、DWD 重算、DWS 逐级刷新,不能跨层直接改数。
-- 重刷 DWS 之前,先把依赖的 DWD 分区重建 INSERT OVERWRITE TABLE dws.dws_order_1d_di PARTITION (dt='${bizdate}') SELECT user_id, COUNT(DISTINCT order_id) AS order_cnt, SUM(sku_num) AS goods_cnt, SUM(sku_num * sku_price) AS gross_amount FROM dwd.dwd_order_detail_inc WHERE dt = '${bizdate}' GROUP BY user_id;这里的参数说明:PARTITION (dt='${bizdate}') 里的 bizdate 是调度系统传入的业务日期,重刷时必须保证 ODS 对应分区数据已经就绪。如果只刷 DWS 而不看 DWD 是否更新,就会出现“上层指标已经变了,明细还是旧数据”的口径不一致问题。面试回答时补一句“重刷按依赖顺序即 ODS → DWD → DWS → ADS 逐层执行”,基本就能完整覆盖考点了。
3. 数仓建模面试题怎么答:维度建模、事实表与缓慢变化维度
3.1 先分清“维度”和“事实”:这张表到底该放哪边
数仓建模面试题没有标准答案,面试官更在意你是否理解某种建模选择的代价。“这个字段要不要拆进维度表”这类题目,最佳回答不是直接给答案,而是先讲三件事:事实表的粒度、维度表的属性、查询时由谁驱动。下面这一组对比可以直接当作答题锚点:
| 维度 | 事实表 | 维度表 |
|---|---|---|
| 内容 | 业务事件、度量值 | 描述性属性 |
| 数据量 | 巨大,随时间膨胀 | 相对稳定,缓慢增长 |
| 主键 | 复合键(多个外键组合) | 单一代理键 |
| 更新方式 | 增量追加为主 | 低频更新 |
| 典型例子 | 订单明细、支付流水 | 用户资料、商品分类 |
答题时把“数据量”和“主键”当成入口:事实表不能用业务主键做主键,因为同一订单会被拆成多行;维度表则恰恰相反,需要唯一键来支撑关联。
3.1.1 用一条 SQL 验证事实表粒度:复合主键是否唯一
在建模面试的实操环节,常被要求“设计一张事实表并说明粒度”。粒度指一行记录代表的含义:订单明细表每行代表“一个订单的一个商品”,而不是“一个订单”。如果粒度不确认,后面所有聚合都会出错。下面这条 SQL 用来验证复合主键是否真正唯一:
-- 验证事实表粒度:若出现重复则说明主键组合不唯一 SELECT order_id, sku_id, COUNT(*) AS cnt FROM dwd.dwd_order_detail_inc WHERE dt = '${bizdate}' GROUP BY order_id, sku_id HAVING COUNT(*) > 1;查询返回记录,说明这张表的最小粒度不是 order_id + sku_id,可能存在同一用户同一天对同一商品多次下单,但缺少渠道或订单行号维度。面试作答时忌讳只说“保证主键唯一”,要主动讲“先确定业务过程的事实粒度,再去找能唯一标识一行的维度列组合”。能说出这一层,就已经区别于只会背定义的候选人。
3.2 缓慢变化维度的处理策略:用户改手机号到底该不该覆盖
线上用户表经常出现电话号码、所在城市变化,数仓建模里处理这类属性变化就是“缓慢变化维度”考题。SCD1 直接覆盖原值、SCD2 保留完整历史、SCD3 保留当前值和原值,三者的适用边界要分清:
| 策略 | 做法 | 适用场景 | 主要代价 |
|---|---|---|---|
| SCD1 | 直接覆盖原值 | 属性价值低、不需要回溯 | 历史彻底丢失 |
| SCD2 | 新增一行,记录生效与失效时间 | 需要准确历史统计、拉链表 | 表膨胀、查询变复杂 |
| SCD3 | 新增一列存原值 | 只需最近一个历史值 | 只能回溯一层 |
用一个用户修改城市为例:如果业务要统计“用户所在地与下单地的匹配关系”,必须用 SCD2,因为 SCD1 覆盖后无法重算历史区间;如果只是展示当前城市,SCD1 就够用。老手一般会被追问“SCD2 拉链表怎么更新”,答题时补一句:批量更新时先动态关掉前一天分区,即把 end_date 设为当天,再插入新的有效分区,而不是物理覆盖历史数据。
3.2.1 拉链表更新的最小 SQL 框架
-- 关闭当天之前仍在生效的旧版本 UPDATE dim.dim_user_scd2 SET end_date = date_sub('${bizdate}', 1) WHERE user_id IN (SELECT user_id FROM tmp_user_new) AND end_date = '9999-12-31'; -- 插入新版本,保留历史记录 INSERT INTO dim.dim_user_scd2 SELECT user_id, user_name, city_id, '${bizdate}' AS start_date, '9999-12-31' AS end_date FROM tmp_user_new;关于参数说明,end_date 统一用无限大的 9999-12-31 表示当前生效版本是数仓里极常见的约定;date_sub 的减一天是为了闭合时间区间,避免新旧版本重叠。实际生产里这两条语句会在同一个事务中执行,保证不会被调度中途打断造成状态不一致。
3.3 星型建模 vs 雪花建模:为什么数仓里星型更多
星型模型维度表保持冗余,雪花模型把维度表继续规范化成多张关联表。数仓面试题的常见问法是“为什么多数场景下选星型”,标准回答必须包含“取数快、易理解、关联少”三个关键词。还有一个容易被忽略的点:OLAP 场景的加载策略是先宽后窄,尽量在建模阶段把维度展开到事实表附近,由上层引擎按需裁剪。
-- 将雪花模型中的多张维度表展开到用户维表上,减少 join 次数 SELECT o.order_id, o.user_id, u.user_name, c.city_name, c.province_name FROM dwd.dwd_order_inc o LEFT JOIN dim.dim_user u ON o.user_id = u.user_id LEFT JOIN dim.dim_city c ON u.city_id = c.city_id;这段 SQL 的效果是把用户和城市两次 join 变成一次,让维度表冗余进事实关联路径。面试时补一句“星型不代表放弃规范化,而是在查询路径和存储冗余之间做取舍”,就能避免被认为只会背结论。如果面试官追问“宽表会不会太重”,可以从“按业务域拆开 + 按复用频次决定是否落宽表”的角度接住。
4. 离线数仓技术栈串讲:Hive、Spark、Kafka 在面试题里的高频考点
4.1 Hive 数仓面试题:分区、分桶、文件格式三选一问到底
数仓面试题的另一大半来自离线技术栈,三个主角是 Hive、Spark、Kafka,2021数仓面试题汇总网上流传的“绝密100个spark面试题”标签也印证了 Spark 必考的地位。Hive 高频题集中在分区与分桶区别、ORC 与 Parquet 选型。面试官常用一个反问试探:“分区字段和分桶字段有什么区别?”答案是:分区按 dt 等列物理分目录,用于查询裁剪;分桶按列哈希分文件,用于提升 join 和抽样效率;分区字段是伪列,分桶字段必须是真实列。
CREATE TABLE hive_order_detail ( order_id STRING, user_id STRING, sku_id STRING ) PARTITIONED BY (dt STRING) CLUSTERED BY (user_id) INTO 64 BUCKETS STORED AS ORC;追问通常会落在“桶数和并发怎么设”。可以顺势回答:CLUSTERED BY 指定桶数,实际 Reduce 任务数要结合桶数、数据量和 Spark 的 shuffle 分区数一起考虑,盲目开大并发反而会产生大量空文件和极小文件。Hive 侧的表还要关注小文件问题,常见做法是设置hive.merge.mapred.files=true或通过中间层合并,回答里带上这句更显实战经验。
4.2 Spark 面试题:宽窄依赖与数据倾斜的答题角度
Spark 面经里最常翻车的是一组概念题:宽依赖和窄依赖。窄依赖指父 RDD 每个分区最多被子 RDD 一个分区使用,宽依赖则相反。面试官真正想听的是对 shuffle 的影响:窄依赖不触发 shuffle,宽依赖会触发 shuffle。2026 年还在考这道题,说明多数候选人对“为什么需要 shuffle”没有现场推导能力。
数据倾斜是这一块的必追问题,常用处理办法有四条:加随机前缀打散 key、提高 shuffle 并行度、广播变量 join 小表、对倾斜 key 单独走一条聚合分支。拿一条实际 SQL 来说:
-- 处理订单表与用户表 join 用户侧数据膨胀的问题 -- 先用窗口函数定位热 key,再决定是否走广播或拆分 WITH user_cnt AS ( SELECT user_id, COUNT(*) AS order_cnt, ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) AS rn FROM dwd.dwd_order_inc WHERE dt = '${bizdate}' GROUP BY user_id ) SELECT /*+ BROADCAST(u) */ order_id, o.user_id, u.user_name FROM dwd.dwd_order_inc o LEFT JOIN dim.dim_user u ON o.user_id = u.user_id;注意这里的 BROADCAST 提示只对小表有效,如果 dim_user 本身超过广播阈值,这个 hint 会被引擎忽略。答题时强调“先定位倾斜 key,再决定用哪条策略”,比照背随机前缀写法更稳。可以补充一句:HBase 在数仓链路中有时候承担维度查询加速的角色,把热维度的实时查询从离线表分离,但 2021 年这批离线面试题里它更多作为选型讨论出现,不需要深入。
4.3 Kafka 面试题及答案:从消息丢失到精确一次语义的递进回答
Kafka 在数仓中的角色是从业务系统同步数据到 ODS 的管道。面试题围绕三件事:消息不丢失、幂等、事务。高效回应分三段答:生产端开启 acks=all 并配合重试;服务端设置副本因子和最小同步副本;消费端处理成功后再手动提交 offset。
# 生产端关键必填参数 acks=all retries=3 enable.idempotence=true # 服务端关键参数 replication.factor=3 min.insync.replicas=2 # 消费端必须手动提交 enable.auto.commit=false参数说明里的逻辑是:acks=all 保证 leader 和 ISR 中副本都写入成功;min.insync.replicas=2 则让至少两个副本确认,防止单点宕机后丢数据。面到高阶会追问“acks=all 为什么还不够”,你需要补一句:ack 只能说明 broker 接收成功,如果消费者在处理完数据但还没提交 offset 时宕机,重新消费会再次处理同一条消息,所以下游写入必须幂等。数仓实时链路里常见做法是用 Kafka 接 Flink,再通过 Flink 的 checkpoint 配合 Kafka 事务实现端到端精确一次,这一步可以作为加分闭环。
5. 背题之外的加分项:把2021数仓面试题汇总变成自己的追问脚本
八股文背一百遍,不如把一道题拆成三层来答,这层能力可以自己训练。拿到 PDF 里任何一道题,不要直接看答案,而是按“结论 → 代价 → 反例”三层组织。比如看到“为什么数仓要分层”,先答结论“为了复用与血缘清晰”,再补一句“但分层会带来加工延迟和存储冗余”,最后说“如果公司报表实时性要求极高,可以适当压缩层数”。三层都答到,面试官基本没有继续追问的空间。
实操时准备一个追问脚本,拿纸笔给每道题写三个“假如”:假如数据量扩大一百倍怎么办、假如源头字段口径变化怎么办、假如查询特别慢怎么办。对应关系如下:
| 原题 | 第一层追问 | 第二层追问 | 第三层追问 |
|---|---|---|---|
| ODS 和业务库区别 | ODS 为什么要保留原始数据 | 重刷历史怎么保证不丢 | 源表删字段怎么兼容 |
| 事实表粒度怎么定 | 同一订单多商品怎么拆 | 复合主键不唯一怎么办 | 迟到的数据怎么处理 |
| SCD2 拉链表怎么更新 | 历史分区关闭失败怎么办 | 拉链表越拉越长怎么归档 | 与实时维度怎么对齐 |
这样一个题变成三个题,2021数仓面试题汇总里两百道题就能扩展成六百个自测点。注意每道题的答案都要求给出“先判断场景,再选方案”的句式,例如先问“这个属性需要回溯历史吗”,再决定 SCD1 还是 SCD2。不要上来就背“我们用的是 SCD2”,这会显得像复读机。
最后建议每周末挑一道题做一次“限时口述”,打开手机录音,三分钟内讲完且不卡壳,再回放检查有没有口头禅和含糊用词。面试的本质是表述的稳定性,知识库已经在这份 PDF 里,能把稳定输出练出来的人,通过率明显更高。
本文还有配套的精品资源,点击获取