简介:面向数据仓库方向求职者与初级数仓工程师的面试题整理文档,针对岗位常见考点做系统梳理,适合面试前集中复习与知识点自查。内容覆盖数据仓库的定义与四大特点(主题性、集成性、稳定性、时变性)、ETL 的抽取—清洗—转换—加载流程与日志告警机制,并延伸到 STG、ODS、DW、Presentation 四层模型的职责划分与开发步骤,以及数据清洗、规范化、代理键与缓慢变化维等维度建模细节。压缩包共 1 个文件,为 docx 格式文档,体积约 31KB,轻量易读,可直接在 Word 中检索关键词、整理个人笔记。目前已有 1711 人学习下载,说明其考点覆盖得到一定认可。读者可据此建立从概念到分层落地的完整知识框架,也可作为面试答题话术与项目经验描述的参考素材。
1. 数据仓库到底是什么:为什么公司里总有一块「只读的老账本」
家里平时记账,收银小票塞满抽屉,月底想算清楚“今年哪类开销涨得最快”,翻小票会翻到崩溃。数据仓库干的就是这件事:它把散落在各个业务系统里的数据抽出来,洗干净,按“客户、订单、商品”这类主题重新摆好,存成一份份历史快照,专门给分析决策用。一句话说,数据仓库是一个面向分析、保留历史、跨系统整合的数据集合。它和天天记账的业务库不是一回事:业务库负责把每一笔交易记下来,数据仓库负责把很多年的账翻出来看规律。搞懂它,面试里绕不开,工作里也少踩坑。
2. 为什么不能直接查业务库:四个特点与ETL的分工
2.1 主题性:先把「要分析什么」定下来
业务数据库通常按项目或功能组织:订单库管订单,客户库管客户,库存库管库存。每张表服务的是某一个业务动作,表与表之间靠外键和业务代码串起来。数据仓库不这么摆,它按主题组织。主题就是使用者真正关心的分析对象,比如客户主题、订单主题、商品主题。所有从不同源系统抽来的数据,都要在较高的抽象层次上围绕这个主题重新整合。
打个比方,业务库像医院里按科室排布的房间,挂号、化验、药房各管一摊;数据仓库像按病种建立的研究档案,把某个病人跨科室的记录收拢到一起。主题性带来的直接变化是:表名不再跟着源系统走,字段命名、取值口径要统一。也正因为这样,数据仓库里经常要先做“一致性维度”,让客户在订单系统里的编号和客服系统里的编号能对上。
主题性还决定了一件事:数据仓库不是把源系统原样复制一份。源系统里那张表叫什么都行,进了数据仓库,得按分析主题重新命名、重新归类。这一步做不好,后面报表口径就会打架。
2.2 集成性:ETL不是搬运,是翻译和清洗
数据仓库的数据来自多个数据源,原始数据格式、编码、粒度都不一样。集成性说的就是把这些数据抽出来、洗干净、转换成统一口径再加载进去。这个过程就是ETL:Extract抽取、Transform转换、Load加载。很多人把ETL理解成“把数据搬过去”,真干起来才发现,搬运只占一小半,大部分时间花在翻译和清洗上。
抽取阶段先要摸清家底:数据从几个业务系统来,各系统跑的是什么数据库,有没有手工数据,手工数据量多大,有没有非结构化文件。摸清楚之后再设计抽取方式。同构数据源可以建直接连接,写查询语句抽;异构数据源可以通过通用数据库接口建链接,或者先把源数据导出成文本、表格文件再导入;文件类数据源可以培训业务人员按模板导入指定库,再用工具抽。数据量大时还得考虑增量抽取,常见做法是用业务发生时间做增量标志,每次先取目标库里最大的时间,再去源系统取大于这个时间的记录。
转换阶段是ETL的重头戏。数据清洗要处理三类不符合要求的数据:不完整的数据,比如供应商名称、客户区域缺失;错误的数据,比如数值字段里混进全角字符、前后有不可见字符;重复的数据,比如多个源系统维护了同一个客户。清洗出来的问题数据通常要反馈给业务部门确认,是过滤掉还是修正后再抽。数据转换则包括不一致数据转换、粒度转换和商务规则计算。比如同一个供应商在结算系统里编码是xx0001,在客服系统里是yy0001,进仓库后要统一成一个编码;业务系统存的是每笔订单明细,仓库里可能要按天、按月聚合;一些指标不是简单加减能算出来的,得在ETL里算好再存进去。
下面这段SQL示意的是“统一性别取值”的转换,源系统里有的用0和1,有的用man和women,进仓库前统一成m和f:
-- 源表 src_customer 中 gender 取值混乱,先做标准化映射 SELECT customer_id, CASE WHEN LOWER(TRIM(gender)) IN ('0', 'm', 'man', 'male') THEN 'm' WHEN LOWER(TRIM(gender)) IN ('1', 'f', 'women', 'female') THEN 'f' ELSE 'u' -- 无法识别的先标记为未知,交由数据质量规则处理 END AS gender_std, load_time FROM src_customer;这段代码里,TRIM去掉前后空格,LOWER统一大小写,CASE把不同源系统的取值映射到标准值。ELSE 'u'不是随便写的:清洗规则里最怕把异常值悄悄丢掉,先标成未知,后面才能统计有多少条没映射上。实际开发中,这类映射通常不写死在SQL里,而是维护一张映射表,方便业务随时补充新取值。
加载阶段看起来简单,其实也有讲究。全量加载适合数据量不大、变化频繁的维表;增量加载适合事实表和大型历史表。加载时还要考虑顺序,维度表没加载完,事实表就找不到代理键。集成性做得好不好,直接决定后面分析结果可不可信。
2.3 稳定性与时变性:一边冻结历史,一边接新数据
数据仓库的稳定性,说的是入库后的历史数据不允许随便改。使用者只能通过分析工具查询和分析,不能像业务系统那样直接更新某条记录。这一点和业务库差别很大:业务库里客户改了地址,直接覆盖原记录;数据仓库里,昨天的客户地址要保留成历史快照,今天的新地址作为新数据进来。
时变性说的是数据仓库会定期接收新的集成数据,反映最新变化。这两点听起来矛盾:一边说不能改,一边说会变。其实不矛盾。稳定性约束的是“已经入库的历史快照”,时变性描述的是“新数据会按周期进来”。实现方式常见的有两种:按日期分区,每天的新数据进新分区,旧分区不动;或者用拉链表,变化的数据关掉旧链、开新链,历史记录仍然保留。
提示:稳定性不等于数据永远不删。实际项目中会有归档和清理策略,但清理的是过期分区或低价值历史,不是随手改某条历史记录。
理解了这一点,就能明白为什么数据仓库里经常要设计“增量时间戳”“开始日期”“结束日期”这些字段。它们不是为了好看,是为了让“当时是什么状态”这个问题有答案。
2.4 ETL日志与告警:别让管道坏了没人知道
ETL是数据仓库的后台管道,管道跑没跑完、哪一步卡住了,得有记录。ETL日志一般分三类:执行过程日志,记录每一步的起始时间、影响行数,像流水账;错误日志,记录出错时间、出错模块、错误信息;总体日志,只记开始时间、结束时间、是否成功。如果用了ETL工具,工具本身也会产生日志,这部分同样要纳入管理。
光有日志还不够,ETL出错时要能通知到人。常见做法是给系统管理员发邮件,附上出错信息和相关日志片段,方便排查。很多团队吃过亏:夜里跑批失败,第二天报表出来是空的,业务先发现,技术后知道。日志和告警做扎实,相当于给管道装了黑匣子,出问题能回看,不用靠猜。
3. 四层模型怎么运转:STG、ODS、DW、DM各管一段
3.1 STG层:先把原始数据接进来,别急着改
STG层是贴源层,模型和源系统保持一致,做增量或全量抽取,不做数据转换,只添加审计列。它的作用不是“把数据变干净”,而是先把数据接进来,减轻源系统压力,留一份备份支持重跑,顺便做数据质量检查。很多项目里,STG层保留7天到一个月的数据,够排查问题就行。
开发STG层一般按这几步走:先确定CDC策略,根据源系统情况选择增量还是全量;再设计mapping文档,写清楚源字段到目标字段的对应关系;然后设计物理模型,通常包括源系统所有字段和审计字段,比如源系统名称、源表名称、加载时间、加载方式,可以去掉主键、索引、默认值这些约束;抽取时优先用ETL工具,因为STG层面向异构数据源,工具对多种数据源支持更好;加载后按保留策略清理。
这里有个容易翻车的点:源表和目标表数据库类型不同时,字段长度要扩充,目标表数据类型选常用几种,长度选固定几档。不要直接照搬源系统的长度,否则中文、特殊字符一进来就可能截断。
3.2 ODS层:全量历史与逻辑删除标记
ODS层把STG层的数据做历史存档,保留源系统的所有历史数据。它和STG层最大的区别是:ODS层加了逻辑删除标记和增量时间戳。很多源系统会做物理删除,即使有逻辑删除标记,也可能在后台人工删数据。所以ODS层加载时要进行数据比较,判断有没有物理删除,有就打上删除标记,保证全量数据可追溯。
ODS层的作用有三个:全量存储源系统数据,支持下游系统实时查询服务,做数据质量检查。开发步骤包括设计mapping文档、设计物理模型、从STG层抽取数据、加载并比较删除情况。同一个数据平台上,从STG到ODS可以用ETL工具,也可以手工编码。如果是流式数据,可以跳过STG层,直接同步到ODS层。
注意:逻辑删除标记不是“真删”。它的意义是让下游知道这条记录在源系统已经不存在了,但历史分析仍然能查到它曾经存在过。
3.3 DW层:清洗、规范化、一致性维度
DW层是数据仓库的核心工作区,做清洗、规范化、提交一致化维度和事实,建立反规范化的维度模型。数据清洗是发现数据质量问题并纠正的过程,通用思路可以借用戴明质量环:计划、实施、监控、行动。主要步骤是先定义数据质量要求,根据业务需求和数据剖析结果排优先级;再制定测量类型;最后提交测量结果表。
异常数据处理有四种策略:中断处理;把拒绝记录放到错误事件表;只做标记,数据继续处理;纠正数据。这四种策略不是随便选,要看数据用途。比如财务对账数据,错了必须中断;用户行为日志里少量异常,可以标记后继续跑。
规范化要解决的是各业务系统“各自为政”的问题。不同系统在命名、取值上都有自己的习惯,规范化就是经过标准化、去重、合并、拆分、整合,把相同描述对象的不同取值统一成企业标准版本。主要步骤有三步:数据标准化,从内容、格式、命名、计算规则统一成唯一版本;删除重复数据,根据保留规则只留唯一一条;数据共存处理,相同客户号在两个源系统都有联系方式时,按业务规则选择保留哪个源系统的值。
提交维度表主要有几步:确认粒度,维度表的粒度就是业务主键;选择代理键生成器,让ETL工具或数据库生成自增长字段;选择维度表类型,常用缓慢变化维类型1和类型2;增量加载维度数据,每个字段设默认值,首次加载要有一条代理键为-1的默认记录,防止事实表查找不到代理键;生成代理键管道,一般建一个查找维表,存业务主键和代理键的映射关系。提交事实表则包括选择事实表类型、用代理键替换主键、增量加载、提交错误事实表、事实表合并、事实表归档。维度和事实数据修正还包括纠正事实、优化和更正事实表、处理延迟事实、维度重建。
3.4 DM层:聚合与宽表,面向分析出口
DM层根据业务需求把DW层数据做聚合或生成宽表。创建聚合事实表常见有三种方法:增量加载,直接创建聚合表并增量加载;聚合导航,用户通过报表分析工具发起请求时,系统根据请求把基础事实表自动生成聚合数据;物化视图,创建物化视图并定时刷新。聚合事实表的粒度和基础事实表不同,所以还要创建相同粒度的缩小维度表,这些维度表只是基础维度表的缩小版。
DM层不是必须很厚。有些团队把DM层做成一张张宽表,方便报表工具直接取数;有些团队只做少量聚合表,大部分查询仍然落在DW层。关键看使用场景:报表固定、查询频繁,适合预聚合;探索式分析多,预聚合可能白做。
3.5 表的加载顺序:为什么事实表总在最后
数据仓库里的表有依赖关系:子维度表、维度表、桥接表、事实表。加载顺序一般遵循几条原则:子维度表加载成功后,再加载维度表;维度表加载成功后,再加载桥接表;子维度表、维度表和桥接表都加载成功后,再加载事实表。这个顺序可以通过主外键关系来确定。
| 表类型 | 作用 | 加载顺序 |
|---|---|---|
| 子维度表 | 雪花模型中的支架维度,拆分大维度 | 最先 |
| 维度表 | 描述分析对象,提供查询窗口 | 子维度之后 |
| 桥接表 | 处理多值维度或层级结构 | 维度之后 |
| 事实表 | 存度量值和维度外键 | 最后 |
顺序反了会出问题:事实表先加载,维度代理键还没生成,外键只能填默认值-1,后面再补就得重跑。所以跑批任务里,依赖关系要配清楚,别靠人工记。
4. 事实表与维度表:分析模型的两块积木
4.1 事实表:只放数字和事件
事实表是数据仓库里存度量值的地方,通常包含大量行,字段以数字和标记为主,比如收益、数量、支出,或者用来统计的Flag。每个事实表都有一个明确的粒度,粒度就是“一行代表什么”。设计时最忌讳把不同粒度的事实塞进同一张表:订单明细是一行一个商品,订单汇总是一行一个订单,混在一起,求和就会重复计算。
事实表常见有四类。事务粒度事实表,表示特定时间、空间点上的一次瞬间测量,比如销售单,ETL过程中以原子粒度直接迁移。周期快照事实表,表现一个时间段或规律性重复,适合跟踪长期过程,比如银行账户、财务报表,常用月粒度,表里的数据必须符合该粒度。累积快照事实表,资料里叫聚合快照事实表,用于描述有明确开始和结束的过程,比如合同履行、保单受理、工作流,粒度是一个实体从创建到当前状态的完整历史,ETL过程中随业务步骤逐步完善。无事实事实表,每个事实表的粒度是一个事件量测,事件可以发生,但没有具体测量值,比如记录“某用户某天访问了某页面”,没有金额可加。
| 事实表类型 | 一行代表什么 | 典型场景 |
|---|---|---|
| 事务粒度 | 一次业务事件 | 销售单、支付流水 |
| 周期快照 | 一个固定周期的状态 | 月末账户余额 |
| 累积快照 | 一个实体的完整流程 | 订单从下单到完成 |
| 无事实 | 一个事件发生与否 | 页面访问、活动签到 |
4.2 维度表:分析的窗口
维度表可以看作使用者分析数据的窗口。它包含事实记录的特性,有些是描述性信息,有些指定如何汇总事实数据。维度表还包含帮助汇总数据的层次结构,比如商品维度里的品类、品牌、规格,时间维度里的年、季、月、日。
维度类型不少。缓慢变化维是大多数维度都会遇到的,雇员升职、客户改地址,都要处理。快速变化维是变化频率很高的维度,比如产品价格、地产价格,甚至搜索关键词,处理方式要看维度大小:小维度可以用类型2新增行;大维度用类型2会撑爆表,通常用类型3只保留上一个值;更大的维度则把稳定字段和频繁变化字段拆开,做维表分离。大维度是记录上百万甚至上千万的维度,比如客户、产品,需要专门考虑索引和浏览性能。迷你维是把大维度里常用的小部分字段抽出来单独建维,提高查询效率。退化维是放在事实表里的维度键,比如订单号,它本身没有独立维表。
4.3 星型与雪花:维度表要不要继续拆
星型模型里,事实表被维度表包围,维度表不再连接新的表,整个图形像星星。星型模型是反规范化的,维度表有冗余,但很多统计不需要做外部关联查询,效率通常更高。雪花模型里,维度表继续拆分,多个维表通过外键连接,图形像雪花。雪花模型减少数据冗余,但查询时需要关联更多表。
| 对比项 | 星型模型 | 雪花模型 |
|---|---|---|
| 维度表结构 | 不再拆分,直接连事实表 | 维度表继续拆成多层 |
| 数据冗余 | 较高 | 较低 |
| 查询关联 | 较少 | 较多 |
| 典型选择 | 冗余可接受时优先 | 大且相对静态的层次 |
星型和雪花不是谁更高级,是冗余和关联的取舍。冗余可接受、查询性能要求高,用星型;维度层次复杂、存储和一致性要求高,用雪花。实际项目里经常混着用,核心维度用星型,个别大维度做雪花拆分。
4.4 缓慢变化维与拉链表:历史状态怎么留
缓慢变化维有三种常见处理方式。类型1是直接覆盖,用新值代替旧值,简单但丢历史。类型2是增加新行,把当前行状态置为off,设置结束时间戳,同时新增一行状态为on,开始时间戳为上一个记录结束时间加一。类型3是增加新列,给表加一个新列存新值,原来的值保留不变。类型2最常用,因为它能回答“当时是什么状态”。
拉链表是类型2思路的典型实现,适合数据量大、变化比例小、又需要查看历史某个时间点状态的表。比如一张用户表有10亿条记录、50个字段,即使压缩后单表存储也很大,每天新增和变化的有200万左右,变化比例很小。全量快照每天存一份太浪费,拉链表只存变化。
实现上,拉链表一般有start_date和end_date两个字段。先在ODS层建一个变化数据表,存当天的insert和update数据;然后拿变化表和拉链表的历史数据按主键比对,交集部分做封链操作,把end_date改成当前日期;变化数据插入拉链表,新记录的end_date填9999-12-31,start_date填当前日期。
-- 关链:把历史表中仍然有效、但当天发生变化的记录关闭 UPDATE dw_order_his h SET h.end_date = CURRENT_DATE WHERE h.end_date = DATE '9999-12-31' AND EXISTS ( SELECT 1 FROM ods_order_update u WHERE u.order_id = h.order_id ); -- 开链:把当天变化数据作为新记录插入,结束日期用最大日期占位 INSERT INTO dw_order_his (order_id, order_status, start_date, end_date) SELECT order_id, order_status, CURRENT_DATE AS start_date, DATE '9999-12-31' AS end_date FROM ods_order_update;这段SQL里,CURRENT_DATE是跑批日期,9999-12-31是占位结束日期,表示“目前仍然有效”。关链只处理end_date还是占位值的记录,避免重复关链。开链直接把当天变化数据插进去,同一个订单号会有多条历史记录,查询时带上时间条件就能还原某一天的状态。
拉链表也有性能问题。存放五年拉链数据,表会很大,查询会变慢。常见优化有两种:在start_date和end_date上做索引;保留全量拉链表,但对外暴露一张只提供近三个月数据的拉链表,历史查询走归档表。别一上来就给所有表上拉链,变化频率高、查询又少的表,上了反而增加维护成本。
5. 常见误区与易混概念:数据仓库、数据库、数据湖、拉链表别混着说
5.1 数据仓库不等于业务数据库
易混点:都叫“库”,都存数据,很多人以为数据仓库就是大一号的业务数据库。为什么混:表、字段、SQL这些词两边都在用,界面上看都是二维表。怎么区分:业务数据库面向交易,讲究快、准、事务一致,一次处理少量记录;数据仓库面向分析,讲究批量、历史、跨系统整合,一次扫大量数据。业务库通常行式存储,数据仓库常用列式存储和分区。业务库改一条记录就覆盖,数据仓库保留历史快照。
| 对比项 | 业务数据库 | 数据仓库 |
|---|---|---|
| 主要用途 | 支撑日常交易 | 支撑分析决策 |
| 数据范围 | 当前业务数据 | 多源历史数据 |
| 操作特点 | 频繁增删改查 | 批量加载、查询分析 |
| 数据变化 | 直接覆盖 | 保留历史快照 |
5.2 数据仓库不等于数据湖
易混点:都存大量数据,都从多个源系统汇入,听起来像同义词。为什么混:很多团队既建仓库又建湖,边界没划清,说着说着就混了。怎么区分:数据仓库强调先建模再入库,数据在进来时就要清洗、规范化、按主题组织;数据湖更强调先把原始数据存下来,格式可以很多样,用的时候再解释。数据仓库适合口径固定、报表和分析需求明确的场景;数据湖适合原始日志、半结构化数据、探索式分析。两者不是替代关系,很多架构里湖存原始数据,仓库存加工后的分析数据。
5.3 拉链表不等于普通历史表
易混点:都保留历史,都有一堆日期字段,看起来差不多。为什么混:普通历史表每天存一份全量快照,拉链表只存变化,查询结果都能还原历史。怎么区分:普通历史表每天一行全量数据,存储量大,但查询简单,拿某天分区就行;拉链表按变化关链开链,存储省,但查询必须带时间条件,否则一个订单会查出多条记录。拉链表适合变化比例小的表,变化频繁的表用拉链,关链开链操作本身就很重。
5.4 星型模型不等于雪花模型
易混点:都叫维度模型,都有事实表和维度表,面试里经常被拿来比较。为什么混:星型和雪花只差在维度表要不要继续拆,图形上都是一个中心往外连。怎么区分:看维度表是否直接连事实表。星型的维度表不再拆,反规范化,冗余多但关联少;雪花的维度表继续拆,冗余少但关联多。冗余可接受、查询性能优先,选星型;维度层次复杂、存储敏感,选雪花。别把“雪花”当成更高级的模型,它只是另一种取舍。
5.5 事实表不等于明细表
易混点:事实表里存了很多行,看起来就是明细数据,和业务明细表差不多。为什么混:两者都可能有订单号、商品号、金额这些字段。怎么区分:事实表有明确粒度,有维度外键,度量值可加或可统计;业务明细表是源系统流水,字段跟着业务流程走,不一定有维度建模。事实表是从明细数据加工来的,但加工过程中会统一粒度、替换代理键、处理异常。把业务明细表直接当事实表用,最常见的问题是粒度不统一,求和结果翻倍。
6. 什么时候该上数据仓库:一个订单分析场景的边界判断
6.1 适合上的信号
如果出现这几个信号,数据仓库值得认真考虑:数据来自多个业务系统,口径经常打架;需要看历史趋势,业务库只保留当前状态;分析查询一跑就拖慢业务库;报表需求多,每个团队都在重复抽数。这时候,数据仓库的价值不是“存得更多”,而是“口径统一、历史可查、分析不影响交易”。
6.2 不适合硬上的信号
反过来,单源系统、数据量不大、报表需求简单,直接读业务库或做一个轻量数据集市就够了。实时性要求极高、每秒都要出结果的场景,传统批量数据仓库也不合适。还有一类情况更常见:数据治理意愿不足,源系统字段没人解释,业务口径没人拍板。这时候上仓库,只是把混乱搬了个地方,后面该踩的坑一个不少。
6.3 一个简化例子:订单主题从抽取到报表
假设某公司要分析订单履约情况,数据来自订单系统、支付系统、物流系统。STG层先把三边的原始数据按天抽进来,不做转换,只加加载时间和源系统名。ODS层保留全量历史,给删除的记录打逻辑删除标记。DW层做清洗和规范化:订单状态统一编码,支付金额统一币种,物流时间统一时区;然后建订单事实表、客户维度、商品维度、时间维度。DM层按天聚合履约时长、支付成功率,生成宽表给报表工具。加载顺序上,先子维度、再维度、再桥接表、最后事实表。
这里最容易翻车的地方不是SQL写不出来,是口径没定。比如“履约完成”是按发货算还是按签收算,订单系统、物流系统、业务部门三边理解不一样。我吃过一次亏:报表跑出来履约率98%,业务一看说不对,他们把取消订单也算进去了。从那以后,我每次做订单主题都强制先拉业务、源系统、报表三方把指标口径写进mapping文档,再动手建表。这份资料把数据仓库的常见概念、分层、维度建模和面试问答整理成了文档,需要系统过一遍的可以拿去对照着看。希望帮到你。
本文还有配套的精品资源,点击获取