ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

数据仓库工程师校招笔试备考:维度建模、SQL与数仓分层实战解析

数据仓库工程师校招笔试备考:维度建模、SQL与数仓分层实战解析 1. 笔试前的准备与岗位认知1.1 数据仓库开发工程师在校招中到底考什么先说一个很多人容易误解的地方数据仓库开发工程师的笔试和普通后端开发工程师的笔试有本质区别。后端笔试重算法、重系统设计而数仓岗的笔试更重数据建模思维、SQL功底、以及对整个数据链路从采集到调度到应用的理解。我当时投搜狐畅游这个岗位时第一反应也是去刷LeetCode结果刷到一半发现方向不太对。翻了几份往年面经之后我调整了策略把精力集中在SQL窗口函数、维度建模理论、Hive/Spark的底层原理这三块。后来笔试实际做下来这个判断基本准确——算法题有但占比不高更多是考察你面对一份数据需求时能不能把它拆成一个清晰、可维护、经得起业务变化冲击的数据模型。另外游戏公司的数仓岗有个特别之处数据量级大、业务指标多、单款游戏的生命周期数据要持续分析。这意味着笔试题目往往会结合游戏业务场景来出比如玩家充值、留存、活跃、关卡流失等等。如果你完全没接触过游戏数据至少要提前了解几个常见指标的含义不然题目都看不懂。1.2 带着“游戏业务”视角去准备搜狐畅游做游戏这个背景非常关键。数仓开发工程师在游戏公司里日常面对的不是“电商订单”就是“玩家行为”而笔试题目自然也会往这个方向靠。我当时把游戏数据分析里最核心的几个指标整理了一遍DAU/MAU日活/月活、新增玩家、留存率次日、7日、30日、付费率、ARPU每用户平均收入、ARPPU每付费用户平均收入、LTV生命周期价值、关卡通过率。后来笔试里确实出现了类似场景不是直接考这些指标的定义而是要求你围绕“玩家充值分析”或“用户留存分析”去设计维度表和事实表。这里分享一个我的备考方法不要只看理论自己动手画一遍星型模型。找一张白纸以“玩家充值”为事实画出它关联的维度——玩家维度设备、渠道、注册时间、时间维度充值日期、充值时段、游戏维度区服、角色、付费维度支付渠道、充值档位。画完再想想如果我要分析“某渠道的新玩家在7日内的充值转化情况”应该怎么关联这些表这个思维过程比背十遍概念都管用。2. 笔试真题复盘维度建模与核心概念2.1 必考的维度建模题用户订单分析怎么答笔试里有一道印象很深的题要求设计一个“用户订单分析”的数据仓库模型核心维度表和事实表都要给出设计。这题算是数仓岗的经典母题几乎每家公司在校招里都会出变体。我当时是这么拆解的订单分析的核心事实是“订单事实表”粒度定在“订单行”——也就是一张订单里的一件商品。为什么不是一单一行因为一个订单可能包含多个商品如果粒度定在订单级别就没法分析商品维度的数据了。定了粒度之后事实表的度量字段就是订单金额、商品数量、优惠金额、实付金额。这几个字段里实付金额是我们日常分析最常用的因为它才代表真实的收入。维度表方面我设计了四个核心维度用户维度、商品维度、时间维度、渠道维度。用户维度表的梯度缓慢变化问题很重要——用户注册时的城市和现在的城市可能不一样这就需要用到缓慢变化维SCD策略。我当时在笔试题里写的是SCD2类型保留历史版本这样既能分析当前状态也能回溯历史状态。商品维度相对简单主要包含商品名称、品类、单价等信息。时间维度在ODS层往往是datetime类型但在维度表里需要拆成年、月、日、周、是否节假日等字段方便按不同时间粒度聚合。面试官在看这类设计题时最关注两件事第一你能不能清楚地说明粒度定义第二你知不知道维度建模中的“可加性”概念。订单金额是可加指标可以直接SUM但比率类指标比如ARPU就不能直接SUM必须先汇总分子分母再计算。我特意在回答里强调了这一点能明显感觉到面试官的认可。2.2 事实表与维度表的区分题最容易被绕进去笔试中还有一类选择题很阴险给出一堆字段让你分辨哪些是维度属性、哪些是度量值。看起来简单但一旦字段多了很容易被绕进去。我的判断方法很简单看到一个字段时先问自己“这个字段能不能直接被SUM/AVG/COUNT”。如果能大概率是度量值如果不适合聚合而更像是描述性的属性那就是维度属性。比如“商品价格”它是度量吗不对商品价格是维度属性。订单金额才是度量。价格是商品的固有属性金额才是订单行为产生的结果。再比如“优惠金额”这个是可以被SUM的度量值但它有一个特性——它既可以是正数优惠减少也对应负数比如退款调整这类“半可加性”指标在建模时要特别标注。另一个陷阱是“时间”字段。很多新人会把“下单时间”当成维度字段直接放在事实表里。实际上如果你只有一张小表这样做没问题。但在正规的数仓架构里时间应该单独做维度表因为在按天、按月、按年聚合时时间维度的属性非常多是否周末、是否节假日、所处季度都塞在事实表里会让表变得很臃肿。事实表只保留一个时间外键关联到时间维度表。还有一类问题是考察“退化维度”的。比如订单号它本来是一个维度但因为太唯一、基数太大不适合单独建一张维度表。所以往往直接放在事实表里作为退化维度。这个概念的考察频率也很高提前理解“为什么订单号不建维度表”这个问题能帮你应付不少变体题。2.3 缓慢变化维SCD的经典处理策略SCD是数据仓库里最经典的理论题目之一笔试必考。我记得当年的题目是玩家在游戏里修改了昵称数仓里应该怎么处理这个变化我直接答了三种策略SCD1直接覆盖保留最新值适合不需要追溯历史的属性比如玩家性别即使有变化通常不关心历史SCD2新增一条历史记录用生效时间和失效时间标记版本适合需要追溯历史的场景比如玩家所属的新手引导版本SCD3新增一个“变化前值”字段保留上一次的旧值适合只关心当前和上一次对比的场景。游戏业务里最常用的是SCD2。因为分析“某个玩家在改昵称前后的行为差异”或者“某版本活动上线后的表现”都需要完整的历史版本。而且一个玩家改一次昵称就多一条记录数据量增长有限对存储和查询压力都不大。但有一个细节我要提醒SCD2的核心是两个时间字段——生效时间和失效时间当前有效记录的失效时间一般设为9999-12-31或NULL。这里有个设计经验如果你用的是Hive建议用9999-12-31而不是NULL因为Hive对NULL的过滤性能不如对常量字符串的过滤性能尤其在分区裁剪场景下。另外最好再加一个“当前是否有效”的标志位查询时直接where is_current Y比判断时间范围快得多。3. 笔试中的SQL与大数据组件考察3.1 SQL题开窗函数是重中之重数仓岗笔试SQL题是重头戏而开窗函数几乎必考。我记得当年有一道题统计每个玩家在每个游戏区服的最新登录时间。这个问题用group by其实做不出来因为你既要分组又想保留非聚合字段。正确写法就是用row_number()或rank()开窗select player_id, server_id, login_time from ( select player_id, server_id, login_time, row_number() over (partition by player_id, server_id order by login_time desc) as rn from player_login_log ) t where t.rn 1这类题的变形有很多取每个部门工资最高的员工、求连续登录天数、计算同比环比增长率。每一种都离不开date_sub、lag、lead这几个函数的组合。我建议把LeetCode数据库板块里面那些“部门工资最高”“连续出现N天”的题目刷两遍笔试基本够用了。还有一个高频考点是“连续登录”。解题思路是先对用户登录日期去重然后通过date_sub(login_date, row_number() over (partition by player_id order by login_date))得到一个分组标志再按这个标志做聚合就能算出一个连续登录区间。select player_id, min(login_date) as start_date, max(login_date) as end_date, count(1) as continuous_days from ( select player_id, login_date, date_sub(login_date, row_number() over (partition by player_id order by login_date)) as grp from ( select distinct player_id, login_date from player_login_log ) t1 ) t2 group by player_id, grp这道题我后来在好几个公司的笔试里都见到过属于性价比极高的一道准备题。3.2 Hive与Spark的考察方式Hive和Spark是数仓开发的两大主力引擎笔试里会从原理层面去考察你是否理解它们的工作机制。Hive常见的考察点是“SQL是怎么变成MapReduce任务的”。简单来说Hive把SQL解析成抽象语法树再经过语义分析生成逻辑计划逻辑算子树然后通过优化器变成物理计划一组MapReduce任务最终提交到Hadoop集群执行。面试官问这个是因为你在日常开发中如果理解了执行流程就能通过观察SQL解释计划来优化慢查询。Spark部分的重点通常是宽依赖和窄依赖的区别、Stage是如何划分的、RDD的惰性求值机制。这背后有一个核心概念窄依赖每个父RDD分区最多对应一个子分区可以在同一个Stage内流水线执行宽依赖涉及shuffle是划分Stage的边界。笔试考这个概念其实就是看你了不了解数据在哪一步发生了重分布——这直接关系到你写SQL时会不会触发不必要的shuffle。还有一类题目直接考优化你写的Hive SQL为什么跑得慢优化方向通常是小表Join大表时把大表放后面或者用MapJoin提示、避免Count(Distinct)在大数据量下造成单Reducer压力、合理设置并行度和内存参数、用分区和分桶裁剪无效数据。如果你的笔试时间比较紧张优先掌握MapJoin和Count(Distinct)替代方案这两个点出现频率最高。3.3 数据倾斜问题的常见问答套路数据倾斜是数仓面试的高频题笔试里也常出简答题。题目描述通常是一个Join任务跑了好几个小时99%的Reduce都完成了就剩一两个一直在跑怎么排查和解决这题的标准答案要分几步走首先确认是否数据倾斜。看application日志里Reduce的耗时分布如果大量任务秒完个别任务卡在99%基本就是数据倾斜。原因通常是Null值过多、关联键分布不均比如游戏表按渠道关联而“官网”这个渠道的玩家数量是其他渠道的上百倍。然后解决方案从几个维度展开一是过滤掉导致倾斜的脏数据比如Null值单独处理不参与Join二是MapJoin优化把小表加载到内存里避免Shuffle适合不等值关联或小维度表三是对倾斜的Key加随机前缀把一个大Key拆成多个小Key再聚合。最后一种方案实际操作起来比较麻烦但笔试答出来会显得你确实处理过真实问题。最后还要补充一句加随机前缀的时机是在Join之前的聚合阶段使用Join完之后要记得去掉前缀恢复原始Key否则下游数据就全是脏的。这个细节我提醒了很多同学笔试千万别答一半。4. 数仓分层与数据治理类问题4.1 数仓分层的标准答案与自圆其说的逻辑数仓分层是笔试简答题的高频题目没有标准答案但你必须有一套逻辑去解释“为什么这样分”。我当时答的是经典的五层结构ODS层数据源原样接入、DWD层清洗、去重、规范化、DWS层按主题轻度聚合、ADS层面向应用的高度汇总数据、DIM层公共维度表。不同公司可能叫法不一样但核心思想是分层不是越细越好而是让每一层都有明确的职责同时控制数据链路的复杂度。为什么需要DWS层这是面试官往往会追问的点也是自圆其说的核心。我的理解是对于重复使用的统计口径比如“当日活跃玩家数”“当日充值金额”如果每次都在ADS层临时去DWD层全量计算不仅慢而且不同分析师跑出来的口径可能对不上。DWS层相当于一个“半成品食材库”按主题维度预聚合好下游ADS层只需要加工成具体报表就行。这个思路在面试里很加分因为我用“做饭备菜”的类比把“为什么要多一层”讲清楚了。另外分层设计里还有一个重要的理念数据流向必须是单向的不允许跨层引用。ODS不能直接被报表查询ADS不能作为下游依赖的源头。这能保证某层重构时不影响其上层的所有应用。4.2 数据质量与血缘治理的实际应用数据质量这块笔试里考的往往不是概念而是场景题。比如发现昨天的报表数据和今天早上跑出来的不一样怎么排查我的排查路径是先确认是不是数据源侧变更了——游戏客户端埋点有没有改动业务库有没有做数据订正。然后看ODS层的抽取任务是否在数据变更之前完成了同步如果上游在抽数过程中发生了主库切换抽出来的数据可能就不完整。接着检查DWD层的清洗逻辑有没有因为新数据格式出现异常而被过滤掉。最后看DWS层的调度依赖如果上游任务重跑下游有没有被正确触发重新计算。血缘治理这道题很多人会忽略但我觉得非常值得展示。我当时主动提到了“数据地图”的概念并分享了一个例子某次任务调度失败后通过血缘关系定位到受影响的报表和下游客群推送而不是一个任务一个任务地手动排查。这个例子能让面试官看出你确实在真实项目中理解过数据治理而不是只背了概念。5. 给后来者的备考建议与心得5.1 笔试时间分配与答题策略搜狐畅游这套笔试题我记得题量不小大概有两个小时包含客观题选择、填空和主观题SQL、设计题、简答题。时间分配上我建议按以下优先级来SQL题优先做因为这是分差最大的部分一道SQL大题写对能顶好几道选择题简答题其次不需要写太长但要把关键点列全分点作答让阅卷人快速抓住重点最后才是选择题因为选择题错了你不知道为什么错但大题没写就是硬性失分。另外有个小心机笔试写设计题时不要只画表结构一定要配上文字说明尤其是你的设计决策。比如“我选择把粒度定在订单行级是因为支持后续商品维度的分析”。阅卷人看的是思路不是格式。你写出来的每一个“为什么”都是在告诉对方你是有经验的而不是背书的。5.2 没有实习经历如何补足项目经验如果你不是科班出身或者简历上数仓相关的实习经历为空笔试这关靠刷题能过关但面试关一定要提前准备项目。我当时做了一个自认为性价比很高的项目叫“用户订单分析数据仓库设计”用一份公开的电商模拟数据集完整地搭建了从ODS到ADS的数仓链路。这个项目的关键不是代码写得有多花哨而是把数据仓库设计的核心环节都走了一遍维度建模确定事实表和维度表明确粒度和度量字段、ETL开发用SQL和Shell脚本实现清洗、转换、加载、SQL分析用开窗函数实现留存分析、复购分析、TopN统计、调度配置用Azkaban或DolphinScheduler把各个任务串起来。面试官问起来你可以很有底气地讲清楚每一步的输入、输出和踩过的坑。这里我特别建议把各类数据文件先做脱敏和清洗确保数据集完整再把整个流程的文档、SQL脚本、调度配置、结果截图整理到GitHub上。面试时直接打开给面试官看“我做了哪些表、为什么这么设计、数据量是多少、优化过什么慢查询”——这种具体到细节的陈述比任何“我懂数据仓库建模”的口头承诺都有说服力。笔试只是第一步真正拉开差距的是你写完一道题之后能不能把背后的设计思路讲清楚。数据仓库这个岗位要的不是会写SQL的“人肉取数机”而是能理解业务、能对数据质量和数据链路负责的工程师。这一点从笔试开始就会体现在每一道题目里。
返回列表