摩拜数据工程师校招笔试题解析:算法SQL与业务场景
2026/9/1 4:39:51 网站建设 项目流程

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_id,Map阶段输出车ID和计数1,Shuffle阶段按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.95,P(上报正常 | 真正故障) = 0.05,P(真正正常) = 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 VIEW,ClickHouse又有很多特殊语法。校招笔试一般不会限制你用哪种SQL,所以写出来的SQL要能保持逻辑自洽。

7.3 笔试现场的做题顺序与心态

拿到卷子后,我习惯先用三分钟快速浏览所有题目,把会做的题目标注出来,最先搞定简单题,这样能稳住心理预期,再攻难题。别在一道算法题上耗超过30分钟,如果实在没思路,先跳过,做后面的SQL题把基本分保住,回头再来想。

提示:笔试前一定要休息好。数据工程师笔试卷的阅读量不小,时间也紧,状态不佳很容易在读题上出错漏。

写在最后:从这套卷子里我看到的数据工程师内核

这套卷子放到今天看,虽然时间过去了好几年,但知识框架并没有过时。数据工程师的核心竞争力从来不是会某个工具,而是能把一个模糊的业务问题拆成数据链路问题,再把数据链路问题拆成可执行的工程落地。算法、SQL、分布式计算、概率统计、业务理解,这几块板缺一块都会在日后的工作里还债。当年我在准备这套试卷的时候,也觉得东西太多太杂,可真正进入岗位后才发现,卷子上考的内容只是起点,工作中面对的场景比想象中复杂百倍。磨基本功这件事,永远都不会亏。

如果你也在准备数据工程师岗位,建议把这套拆解思路当成一个自我对照表,逐个模块检查自己的短板。别只盯着难题偏题,把最常见的窗口函数、数据倾斜、去重统计、调度场景分析练到熟练,拿到offer的概率就会大大提升。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询