ARTICLE DETAIL

资讯详情

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

摩拜数据工程师校招笔试题解析:算法SQL与业务场景

摩拜数据工程师校招笔试题解析:算法SQL与业务场景 2018年前后共享单车正处在最热闹的时期我当时一边忙着刷实习一边关注着各家的校招动态。摩拜的数据工程师笔试卷在那批共享单车公司里算是有代表性的题量不小风格也务实很能反映一家以物联网和海量轨迹数据为核心资产的公司到底想招什么样的数据工程师。今天我把这张卷子按考点拆开结合我当时准备校招的实际经验聊聊每类题目背后的考察逻辑和解题思路。无论你现在是准备面试还是工作中需要补一补数据工程的基础这篇内容都能帮上忙。1. 一套校招笔试卷出题人到底想筛什么样的人很多人在准备校招时习惯一头扎进LeetCode刷题觉得算法就是数据工程师笔试的全部。但如果你认真研究过摩拜这类公司的笔试卷会发现它的考察范围远不止写代码。一套合格的校招笔试卷核心目标是用两到三个小时快速筛选出“能干活、好培养、基础扎实”的候选人。数据工程师这个岗位尤其特殊它夹在软件工程师和数据科学家中间既要懂工程又要懂数据。1.1 数据工程师和数据分析师的考点差异如果你同时看过数据分析和数据工程两套笔试题你会发现差异非常明显。数据分析师的题目偏业务和统计会给你一份订单表让你做留存分析、漏斗转化、AB实验显著性检验重点考察业务理解能力和分析结论的表达。而数据工程师的卷子更偏底层考察的是你怎么把数据准确、高效、稳定地加工出来。比如同样给你一份骑行订单表数据分析师关心的是“哪个区域的骑行量增长了”数据工程师关心的是“这份表怎么从埋点日志清洗出来怎么避免重复记录怎么保证时间字段跨时区不出错多大数据量下用什么样的任务调度策略跑完”。1.2 从摩拜业务反推笔试题型设计摩拜单车在2018年是什么状态全国几百个城市上千万辆智能锁单车每辆车每时每刻都在上报位置、电量、车锁状态订单数据动辄千万级。这样的业务场景决定了数据工程师要处理的核心难题轨迹数据清洗、订单去重、调度统计、车辆状态实时分析。从岗位倒推笔试题型你能看到一个很清晰的逻辑闭环。海量单车数据要求候选人具备分布式计算的基础认知所以会有MapReduce、Hive、Spark的题目订单和轨迹数据要存储和查询所以SQL是必考项异常检测和调度策略需要一定的算法和概率统计基础所以算法题和数学题也不能少。我印象里这套卷子大致分了五个模块题型模块考察重点典型题量数据结构与算法编程基本功、复杂度分析2-3题SQL与数据查询多表关联、去重、窗口函数3-4题大数据基础MapReduce、Hive、Spark概念与原理3题左右概率统计与建模条件概率、期望、简单预测2题左右业务场景设计车辆调度、指标体系搭建1-2题1.3 做题时间分配的策略这套卷子题量不算少但难度分布是阶梯式的。我的建议是第一步先快速浏览全部题目把业务场景题和SQL大题留足时间因为这类题分值高并且容易踩坑。第二优先做算法题虽然写代码耗时间但通常通过的用例越多分越高。最后再回头啃概率统计和概念题这类题目即使答案不够完美只要写出关键公式和思路也能拿到一半分。我见过不少同学在概念题上死磕结果最后一道业务场景题只写了两行字这是非常亏的。校招笔试不是要求你每题都满分而是要在有限时间内拿到总分最优。2. 算法与数据结构共享单车场景下的三个必考套路算法题是数据工程师笔试的重头戏但它的风格和BAT的算法岗不一样。摩拜的算法题更贴近业务不会出那种纯粹为难人的偏题怪题。我把当时反复练习的三类高频题型挑出来说一说这些都是直接从共享单车业务里抽象出来的理解透了比背几十道模板题都有用。2.1 TopK问题找热门骑行起点第一种高频题是TopK问题业务背景可能是这样的给定全城所有骑行订单的起点坐标按网格ID聚合后找出骑行量最大的前10个网格。这类题考察两个核心点一个是统计频率一个是取前K个元素。如果你平常用Python最简单的方法是构建字典统计频率然后排序取前K。但面试官期望你能说出更优的方案。如果数据量大到单机内存放不下呢这就引出了堆的解法维护一个大小为K的小顶堆遍历元素时如果当前频率大于堆顶就替换。这样时间复杂度是O(NlogK)空间复杂度是O(K)相比全排序的O(NlogN)有明显优势。我当时做这类题时习惯同时补充一个条件如果K接近于N堆的优势就不明显直接排序更快甚至可以用快速选择算法做到期望O(N)。在笔试卷上把这种边界情况写出来会给阅卷人留下“考虑问题全面”的好印象。2.2 哈希表与UV去重的隐含考点第二类高频题是去重统计。业务场景通常是统计某天的活跃骑行用户数日志中可能出现同一个用户多次产生订单要求按user_id去重。很多人第一反应是写个Set去重这确实是最容易实现的方案。但笔试如果考到这里多半会追加一问如果一天的数据量有几十亿条单机Set放不下怎么办这时候可以给出分桶加哈希的思路将user_id哈希后模N分到N个桶中每个桶独立计数最后把所有桶的计数加起来。这个过程其实就是MapReduce中ReduceByKey的基本思想。2.3 时间序列排序警惕隐含的稳定性要求第三种题是排序相关的比如按骑行时长对订单排序。看起来很简单但如果数据源被拆分到了多个分区而每个分区有了排序结果最后合并时就要考虑排序的稳定性。稳定的排序算法如归并排序能保证相同骑行时长的订单仍按原始顺序排列这在多级排序场景下特别重要。例如先按区域分组再按骑行时长排序如果第二层排序不稳定同一个区域内的订单顺序可能被打乱。我建议在笔试时额外写清楚时间复杂度和空间复杂度并且说明在什么场景下选稳定排序、什么场景下不关心稳定性这种细节往往是拉开分差的地方。提示校招笔试的算法题一般不要求追求所谓的“最优解”更重要的是把思路清晰表达出来。先用暴力解法想通边界再往上去优化这会自然构成一个层次分明的答题过程。3. SQL题窗口函数、去重和时间粒度是主战场SQL题在摩拜的数据工程师笔试卷里分值占比很高而且很结合实际业务场景。如果你刷过LeetCode数据库题会感觉题目类型很熟悉但这里更看重动手设计复杂查询的能力。3.1 窗口函数解决“每个区域骑行量排名”有一道题我印象很深给定单车订单表bike_order字段包括order_id、user_id、bike_id、start_time、end_time、start_zone_id、end_zone_id要求找出每个区域骑行量排名前三的用户。这是一道典型的窗口函数题目解题核心是用ROW_NUMBER()或DENSE_RANK()按区域分组、按骑行次数降序排名。我当时写的SQL大致是这样SELECT start_zone_id, user_id, ridership_cnt, rn FROM ( SELECT start_zone_id, user_id, COUNT(*) AS ridership_cnt, ROW_NUMBER() OVER(PARTITION BY start_zone_id ORDER BY COUNT(*) DESC) AS rn FROM bike_order GROUP BY start_zone_id, user_id ) t WHERE rn 3;这里有一个非常容易踩的坑就是直接在窗口函数里写COUNT(*)而外层又做GROUP BY导致的逻辑混乱。正确思路是先用GROUP BY算出每个用户在每个区域的骑行次数再用窗口函数做组内排名最后在外面过滤排名。这个“先聚合后开窗再过滤”的顺序就是窗口函数题的标准套路。3.2 COUNT(DISTINCT) 与精确UV的陷阱第二类SQL高频题是去重。给定用户行为日志表user_behavior字段包括user_id、page_name、visit_time、device_id要求统计每天访问首页的独立用户数。刚接触的人会直接写SELECT DATE(visit_time) AS visit_date, COUNT(DISTINCT user_id) AS uv FROM user_behavior WHERE page_name home GROUP BY DATE(visit_time);这道题本身不难但笔试往往会在下一问追加一句如果一天的数据量上亿用COUNT(DISTINCT)处理会有什么问题这就把话题引向了Hive和Spark中HyperLogLog这类近似去重算法。COUNT(DISTINCT)在数据量大的情况下会消耗大量内存而且执行效率很差所以大数据场景会使用approx_count_distinct这样的近似函数用很小的误差换取大幅度的性能提升。3.3 时间字段格式化与时段划分还有一类SQL题考察的是时间处理能力。例如要求按小时统计各区域的订单量并找出早高峰时段7点到9点订单量最大的区域。这里的关键是不要直接在WHERE里对原字段用HOUR()函数过滤因为这样会导致全表扫描无法利用分区。比较稳妥的方式是用BETWEEN限制时间范围再去格式化分组或者在数仓设计时就把小时字段单独拆分出来作为一个维度列。我见过有候选人把时间字段当成字符串拼接日期虽然逻辑上没错但会对时区问题视而不见。实际业务里服务器日志记录的时间是UTC还是本地时间决定了后续所有统计口径的正确性。笔试卷不会明说这一点但如果你能在答案里主动提到时区问题就体现出你的实战经验了。3.4 一个完整的SQL业务场景演示这里把当时练习过的、比较综合的一道题复现一下。表结构是trip_record字段有trip_id、city_id、bike_id、user_id、start_time、end_time、start_lat、start_lng、end_lat、end_lng。题目要求统计每个城市每天的平均骑行时长并按城市和日期排序输出骑行时长最长的前三个城市。解题思路先算出每个城市每天的平均骑行时长然后按日期分组用窗口函数排名WITH daily_avg AS ( SELECT city_id, DATE(start_time) AS trip_date, AVG(TIMESTAMPDIFF(SECOND, start_time, end_time)) AS avg_duration FROM trip_record GROUP BY city_id, DATE(start_time) ) SELECT city_id, trip_date, avg_duration FROM ( SELECT city_id, trip_date, avg_duration, ROW_NUMBER() OVER(PARTITION BY trip_date ORDER BY avg_duration DESC) AS rn FROM daily_avg ) t WHERE rn 3;写作中注意TIMESTAMPDIFF在MySQL里是常用函数在Hive里要用unix_timestamp(end_time) - unix_timestamp(start_time)。这就是大数据SQL和传统SQL的差异笔试答案里如果写清楚不同引擎的适配方案会显得特别专业。4. 分布式计算与大数处理不考框架API考思想摩拜这类公司每天产生的数据量决定了他们不可能用单机处理所以笔试卷里必然会有分布式计算相关的题目。不过我观察到一个现象这部分不考具体的框架API而是考底层思想和问题排查能力。很多只刷过LeetCode的同学在这里容易翻车。4.1 MapReduce原理与Shuffle过程关于MapReduce的题目通常有两种问法。一种是问原理流程另一种是问某个场景怎么设计。问原理时需要说清楚Map阶段、Shuffle阶段、Reduce阶段各自干了什么。Shuffle是MapReduce中最重要也最容易出问题的地方Map端会把输出按key分区、排序、合并Reduce端拉取属于自己的数据后再次合并排序然后传给reduce函数。笔试遇到这种题千万别只背概念最好用共享单车数据举例。比如统计每辆车的当日使用次数key是bike_idMap阶段输出车ID和计数1Shuffle阶段按bike_id分组Reduce阶段把同一个bike_id的计数加起来。这样举例既展示了原理又体现了业务理解。4.2 数据倾斜的定位与处理另一个经典题目是数据倾斜。题目让你分析为什么跑一个Join任务要两个小时大家都会想到数据倾斜但要写出解决方案需要真正的实战经验。通常的思路包括加盐、随机前缀、两阶段聚合、以及调整Join顺序等。我当时回答时会先定位问题是Map端倾斜还是Reduce端倾斜是热点key导致单个Reduce处理了90%的数据。然后提出针对性的方案。如果热点key是少数几个可以对热点key加随机前缀打散到多个Reduce如果整个数据集都有大key就改用广播变量或重新设计Key结构。这个分析过程比单纯写结论显得更完整。4.3 Hive优化与Spark算子选择的工程化思路Hive优化题也很常见比如给了几个慢查询让你分析原因并优化。常见的回答方向有分区裁剪、小文件合并、列剪枝、避免笛卡尔积、使用Semi Join代替IN子查询。这些算不上高深但考察的是平时做数仓开发的积累。Spark部分更偏算子选择。统计每个区域的高频骑行时段用groupByKey可以实现但应该优先用reduceByKey因为后者在Map端会做预聚合能显著降低Shuffle数据量。笔试卷里如果出现这类问题意图不是看你会不会写算子而是看你是否有“数据量一大就想到Shuffle”的工程敏感度。注意应对分布式计算题不要死记硬背概念要用“数据从一个节点流向另一个节点的过程”来理解每个设计。只要你把Shuffle的链路理解了数据倾斜和算子优化就有了自然的基础。5. 概率统计与AB实验基础数据工程师接触统计学的机会看似不多但日常工作中做报表异常检测、指标口径验证、AB实验数据回流背后全是统计知识。摩拜这套题也考了概率统计题目不算难但很能考察候选人是否具备扎实的数学功底。5.1 条件概率与贝叶斯使用场景有一类题会给一个场景某区域单车的平均故障率是2%已知某辆车在上报的状态中显示“正常”但巡检发现该状态上报的准确率是95%问这辆车真正正常的概率是多少。这个问题需要用贝叶斯公式计算先把事件定义清楚A表示车真正正常B表示上报正常。题目给的是P(上报正常 | 真正正常) 0.95P(上报正常 | 真正故障) 0.05P(真正正常) 0.98。套公式得出P(真正正常 | 上报正常) 0.98 * 0.95 / (0.98 * 0.95 0.02 * 0.05)这是一个看似简单、实则在审查你对条件和先验的理解是否清晰的题。很多人写公式很快但会把条件反过来用这就是对贝叶斯公式理解不透彻的表现。笔试时最好把事件定义和公式推导过程写完整不要只给一个结果。5.2 期望值与成本优化另一类考题是关于期望和成本计算。比如单车调度员每天处理800辆车如果每辆调度错误的概率是0.01求那天调度出错次数的期望。这是一道标准的二项分布题期望就是800×0.01 8次。题目可能会继续问如果把调度正确率提升到99.5%期望出错次数降到4次公司每天能节约多少成本。这时需要用单价乘以减少的期望次数。这种题考的不是计算能力而是能不能把业务问题抽象成概率模型。我建议在答卷上先写“假设每次调度是否出错是独立事件则调度错误次数服从二项分布”这句话可以帮你拿到关键步骤分。5.3 显著性检验与实验分流逻辑还有一小部分题涉及AB实验。比如某城市在两个区域分别推行了不同的调度策略想比较用户骑行量是否有显著差异。这里需要明确原假设和备择假设选择合适的检验方法并解释p值含义。数据工程师虽然不直接设计实验但要负责把实验数据正确回流所以至少要知道实验分桶不能按用户数量简单平均要考虑用户行为天差万别的分组是否随机还有即使样本量很大也不一定说明结果显著看的是效应量和置信区间。校招笔试能写出“不能只看均值差异要看置信区间和效应量”这已经是一个加分项了。6. 业务场景题如何给调度系统出一个“最少搬运”方案压轴的业务场景题往往是整套卷子里最拉分也最有意思的部分。它没有标准答案考察的是你分析问题、建立模型、提出方案的能力。摩拜的业务场景题通常围绕“潮汐现象”展开早高峰时大量单车从住宅区流向办公区晚高峰则相反。如果调度不及时就会出现一个区域无车可用、另一个区域车辆堆积。6.1 潮汐现象抽象成数学模型面对这种题不要上来就写方案要先定义清楚问题。可以把城市网格化每个网格是节点每个节点在某个时段有车辆流入量和流出量。某个时间段内网格i的净流入量等于流入订单数减去流出订单数。如果净流入量为正说明车辆堆积如果为负说明车辆流失。用这个模型就能判断哪些区域是需要调度员去搬车的。比如早高峰期间办公区网格的净流入为正住宅区网格为负并且负得越多说明车辆越短缺。调度方案本质上是一个“搬运优化问题”目标是最小化搬运成本的同时尽量让每个区域的车辆数量满足预估需求。6.2 供需不平衡度指标设计抽象出数学模型后进一步需要定义衡量区域健康状况的指标。我在笔试时常用“供需不平衡度”这个思路具体公式是该区域当前车辆数减去预估需求车辆数再除以预估需求车辆数得到一个归一化比率。如果这个比率超过某个阈值比如0.2说明车辆过剩如果低于-0.2说明车辆严重不足。解题时把阈值、数据来源、计算频率写清楚面试官会认为你真的动手思考过问题。调度策略的触发条件也有讲究是按15分钟粒度实时触发还是按小时批量计算取决于运营成本和系统性能之间的平衡。数据工程师在这个问题中给出的方案通常是用Hive或Spark做离线统计再用Redis存实时状态最终呈现在调度平台上。6.3 从考试题到真实工作的距离这道业务题的魅力在于它考察的东西和实际工作非常接近。真到了公司你会面临数据质量没那么干净、标签定义不统一、调度执行在途中可能失败等各种问题。笔试期间只要能把分析框架搭出来展示出逻辑条理就已经能拿到不错的分数。7. 校招准备心得与复盘小技巧回顾整套摩拜2018校招数据工程师笔试卷它没有太多偏题怪的考点整体风格是“数据仓库工程师日常每天都在做的事”的浓缩。如果你正准备数据工程师的校招我的建议是按模块系统性复习而不是东一榔头西一棒子。7.1 算法题与大数题的复习路线算法部分重点放在哈希、堆、排序、双指针上就可以复杂数据结构如红黑树的实现不太会直接手写但要知道原理。大数据部分重点是理解MapReduce和Spark的运行流程建议自己动手写一个简单的WordCount并尝试在本地模拟数据倾斜观察任务运行时间的变化。纸上谈兵学不会真正的性能调优。7.2 SQL的练习方式SQL不要只看题解最好本地装一个MySQL或者直接使用在线SQL练习平台把每一道窗口函数题亲手跑一遍。注意不同数据库的方言差异比如MySQL 8.0才支持窗口函数Hive有LATERAL VIEWClickHouse又有很多特殊语法。校招笔试一般不会限制你用哪种SQL所以写出来的SQL要能保持逻辑自洽。7.3 笔试现场的做题顺序与心态拿到卷子后我习惯先用三分钟快速浏览所有题目把会做的题目标注出来最先搞定简单题这样能稳住心理预期再攻难题。别在一道算法题上耗超过30分钟如果实在没思路先跳过做后面的SQL题把基本分保住回头再来想。提示笔试前一定要休息好。数据工程师笔试卷的阅读量不小时间也紧状态不佳很容易在读题上出错漏。写在最后从这套卷子里我看到的数据工程师内核这套卷子放到今天看虽然时间过去了好几年但知识框架并没有过时。数据工程师的核心竞争力从来不是会某个工具而是能把一个模糊的业务问题拆成数据链路问题再把数据链路问题拆成可执行的工程落地。算法、SQL、分布式计算、概率统计、业务理解这几块板缺一块都会在日后的工作里还债。当年我在准备这套试卷的时候也觉得东西太多太杂可真正进入岗位后才发现卷子上考的内容只是起点工作中面对的场景比想象中复杂百倍。磨基本功这件事永远都不会亏。如果你也在准备数据工程师岗位建议把这套拆解思路当成一个自我对照表逐个模块检查自己的短板。别只盯着难题偏题把最常见的窗口函数、数据倾斜、去重统计、调度场景分析练到熟练拿到offer的概率就会大大提升。
返回列表