2023年春招,腾讯音乐的数据工程岗笔试放在第一批,说实话这个时间点挺考验人的。大部分人的春招节奏还停留在“过完年再说”的状态,结果招聘流程说开就开,不少人是在完全没准备的情况下被拉进考场的。我身边就有朋友考完出来直摇头,说题目看着不难,但时间根本不够用,SQL刚写完一大半,编程题还没碰就交卷了。这篇文章我就结合那批笔试的实际考察逻辑,聊聊数据工程岗的笔试到底在筛什么人、题目背后考的是什么能力、以及怎么在有限时间内把分拿满。
如果你现在正在准备大厂数据工程岗的校招笔试,或者之后打算投腾讯音乐相关的数据岗位,这篇文章会把整个笔试的考察框架、实战策略和容易忽略的细节拆开讲清楚,帮你少走弯路。
1. 数据工程笔试和普通后端/算法笔试,筛选逻辑完全不一样
很多人在准备数据工程岗笔试的时候,容易犯一个方向性的错误:照着后端开发岗或算法岗的题库刷。结果就是选择题里的JVM调优、红黑树旋转看得一头雾水,编程题死磕动态规划,最后真正该拿分的SQL反而没有时间练。
1.1 数据工程岗考核的三个层次
数据工程岗日常做什么?本质上就三件事:把数据从A点搬到B点,过程中保证数据不丢不重不错;把杂乱的数据加工成可分析的形态;让整个数据链路跑得稳、跑得快。因此笔试考的不是某个单一技能,而是围绕这三件事分层次考察。
第一层是数据基础能力。SQL是绝对的核心,这几乎是所有数据工程岗笔试的共识,腾讯音乐也不例外。它考的不只是会不会写select、join,而是能不能处理真实业务里常见的复杂查询场景,比如连续登录、留存计算、同环比、窗口函数排序。
第二层是大数据生态的知识广度。数据工程离不开分布式计算框架,所以Hadoop、Spark、Flink、Kafka这些组件会被反复提及。这个层次的考察方式通常是选择题或简答题,重点看你是否理解这些组件的基本原理、适用场景和常见问题的排查思路。
第三层是编程与工程能力。毕竟数据工程师也写代码,尤其是写数据处理逻辑。笔试里的编程题一般不会出特别偏难怪的算法,更多是考察你用代码解决实际数据问题的能力,比如解析日志、实现一个简单的聚合逻辑。
1.2 腾讯音乐笔试的风格特点
腾讯音乐的笔试整体风格偏向务实,或者说“业务导向”。同一套题里可能会出现某个音乐App的真实业务场景,比如统计某首热门歌曲的播放量、分析用户听歌时长分布,让你在熟悉的业务背景里完成数据处理任务。这种出题方式的好处是,它不是死板地考语法,而是考你面对真实业务需求时能不能快速转化成可执行的查询逻辑。
另一个特点是时间紧凑。整个笔试时间一般控制在90到120分钟,但题量并不算少,要涵盖选择题、SQL题、编程题,有时还会有简答题。如果你在某道SQL题上纠结太久,后面的编程题很可能来不及写。
1.3 岗位JD与笔试内容的对应关系
投递岗位的时候可以留意一下JD里的关键字。腾讯音乐数据工程岗的JD里往往会出现“数据仓库”“ETL”“数据质量”“Spark”“Flink”这些词,这些关键字基本预告了笔试的重点。
如果JD重点提了数据仓库,那SQL题大概率考数仓建模相关的内容,比如维度建模、拉链表设计;如果重点提了实时计算,那Flink相关的知识点肯定会出现在选择题里。拿着JD去反推笔试范围,比漫无目的地刷题高效得多。
2. 题型盘点:选择题、SQL题、编程题和场景题各自的考察重点
根据那批笔试的反馈来看,题型基本固定,但每个题型的侧重点和平时刷题的感觉不太一样。我先逐类拆一遍。
2.1 选择题:大数据组件原理和基础知识覆盖面广
选择题大概占30%左右的分值,覆盖面非常广。我印象比较深的几个方向是这样:
Hadoop相关问得最多的就是HDFS读写流程、NameNode和DataNode的角色分工、MapReduce的Shuffle过程。比如“HDFS默认副本数是多少”这种基础题基本属于送分题,但“MapReduce中Shuffle阶段的作用是什么”这种题就需要你真正理解整个计算流程。
Spark是选择题里的重头戏。RDD、DataFrame、Dataset三者的区别,宽依赖和窄依赖的判断,Stages划分机制,Spark作业提交流程(Client模式与Cluster模式的区别),这些问题反复出现。建议准备的时候重点搞懂“宽依赖和窄依赖如何影响Stage划分”这个点,因为它是很多Spark相关题目的底层逻辑。
Flink在腾讯音乐的笔试里出现频率也不低,毕竟音乐平台的实时推荐、实时榜单都离不开Flink。状态管理、Checkpoint机制、事件时间与处理时间的区别、Watermark的作用,这几个知识点要熟练。有一个很容易考的点是“Flink如何保证精确一次语义”,要能说清楚Checkpoint和两阶段提交配合的原理。
消息队列Kafka同样绕不开。分区与副本的概念、消费者组如何管理位移、消息会不会丢失(生产者、Broker、消费者三个层面分别怎么保证),这些需要理解到位。
还有一个容易被忽视的点是基础数据结构和计算机网络。我见过有选择题考到TCP三次握手、HTTP状态码的含义,如果你大学学的东西忘得差不多了,建议考前快速过一遍,不用太深,但基本概念得捡起来。
2.2 SQL题:笔试题的拉分项,也是刷人最狠的部分
SQL题往往是整张卷子里分值占比最高的单项,而且一旦写错,几乎没有蒙对的概率。那批笔试的SQL题大概有3到4道,难度从入门到进阶递进。
入门题一般是单表查询或简单的两表连接,考最基本的语法,比如按某个字段分组统计、过滤条件组合。这类题是送分题,但要注意别在细节上丢分,比如COUNT和COUNT(DISTINCT)的区别、NULL值的处理方式。
进阶题就开始上难度了。窗口函数是必考的,ROW_NUMBER()、RANK()、DENSE_RANK()三兄弟的区别、SUM() OVER()跑批计算、LAG()和LEAD()取前后行数据,这些是最基础的窗口函数操作。考法通常是这样的:给你一张用户听歌记录表,让你统计每个用户听歌时长的累计值,或者找出每个用户听得最多的前三首歌。
还有一个常考的题型是连续问题,比如统计连续登录N天的用户。这种题用窗口函数做非常方便,核心思路是用日期减去ROW_NUMBER()的序号得到一个分组标识,然后按这个标识分组统计。我第一次见到这个思路的时候觉得非常巧妙,理解了之后SQL能力会有一个明显提升。
那批笔试里还出现了一道被很多人讨论的题:统计每分钟在线听歌人数的峰值。这类问题本质上是个时间区间重叠问题,思路是把每条记录拆成开始事件和结束事件,然后用SUM() OVER()做事件流累加,峰值就是累加过程中的最大值。这类题在LeetCode上叫“会议室II”或者“卡车占用车位”,但套上听歌场景之后对数据工程岗来说更贴切。
2.3 编程题:难度适中,重在实际问题的解决
编程题一般有两道左右,可以用Python或Java写。和算法岗动辄困难难度的动态规划题不同,数据工程岗的编程题更偏向“用代码处理实际数据”,考法通常是:给定某种格式的日志文件或数据流,让你写代码完成某个统计任务。
比如给定一个包含用户ID、歌曲ID、播放时间戳的日志列表,统计每首歌的独立播放用户数;或者给定一批包含开始时间和结束时间的记录,找出所有存在冲突的时间段。这种题要求你熟悉Python里字典、集合、列表的基本操作,能快速把思路转化成代码。
还有一类题是自选实现某种数据结构,比如手写一个带过期时间的缓存、实现一个简单的LFU缓存,这其实是在考察你对生产环境中常见问题的理解,比如需要缓存热点歌单数据时怎么处理过期和淘汰。
需要注意,编程题的判题环境一般比较严格,你要自己处理输入输出格式。很多人不是思路不对,而是卡在了输入输出的解析上,这在平时用LeetCode刷题时不太容易暴露,因为LeetCode已经把输入输出处理好了。
2.4 场景题/设计题:隐藏在简答里的加分项
有些场次会包含一两道简答或场景设计题,比如让你设计一个音乐播放量实时统计方案,或者问你“如果数据仓库里某张表的任务失败了你如何排查”。这类题看起来开放,但其实考察的是你对完整数据链路的理解。
设计实时统计方案时,可以从数据接入(Kafka)、实时计算(Flink)、结果存储(Redis或ClickHouse)三个层面回答,每一步说清楚用的组件和原因。任务失败排查则可以从调度日志、上游依赖、数据倾斜、资源不足几个方向展开。
开放题没有标准答案,但一定要展示出结构化的思维——分步骤、分模块地组织你的回答,让面试官能通过笔试看到你解决问题的思路,这本身就是一种能力证明。
3. 做题顺序和时间分配:决定你能否把会做的题都做完
那批笔试最大的坑不是题目难,而是时间不够用。很多人栽在“先做选择题,再做SQL,最后做编程题”这个顺序上,结果选择题消耗了太多精力,SQL没写透,编程题直接空白。这里我分享一套经过验证的时间分配策略。
3.1 拿到试卷先花3分钟看全貌
不要上来就埋头做题。先花两三分钟把整张试卷从头到尾翻一遍,搞清楚每类题有多少道、每道题大概多少分、难度感觉如何。这能帮你在心理上建立一个全局观,知道哪些题是稳拿分的、哪些题需要冲刺。
一套比较合理的分配方案参考下面这个表格:
| 题型 | 建议用时 | 做题策略 |
|---|---|---|
| 选择题 | 20-25分钟 | 会做的直接选,不会的先跳过,每道题不超过1分钟 |
| SQL题 | 35-45分钟 | 先写有把握的题,难题留到最后 |
| 编程题 | 25-35分钟 | 至少保证一题完整提交,另一题写出核心思路 |
| 检查 | 5-10分钟 | 重点检查SQL字段名、表名、边界条件 |
3.2 做题顺序:先做最优性价比的题
我的建议是,如果试卷整体题量较大,先用几分钟快速扫一遍所有SQL题和编程题,判断哪些是自己有把握的,优先做这些。
原因是SQL题和编程题分值高、区分度强,是决定你能否进入面试环节的关键。选择题就算全对,如果SQL和编程拉胯,总分也不会好看。反过来,如果SQL和编程答得不错,选择题错几道影响不大。
具体操作可以这样:优先做所有你一看就有思路的SQL题,再回头做选择题中会做的题,接着做编程题里最有把握的那道,最后利用剩余时间去磕难题。这个顺序看起来跳来跳去,但确实能在有限时间里拿到最多分数。
3.3 编程题的保底策略:不追求满分,追求有提交
编程题如果完全没写,基本等于直接放弃一整块的分。哪怕时间再紧,也要保证至少一道题有一个能跑通的版本。
一个很实用的保底策略是:先把暴力解法写出来并确保它能正确计算小规模输入。很多时候暴力解法能帮你拿到一部分测试用例的分数,而且能在写的过程中慢慢优化思路。另一个策略是,如果某道编程题完全没思路,就把你能够想到的步骤用伪代码或者注释写出来,同时给出你的基本思路。这在人工阅卷的情况下有一定概率拿到过程分。
我这么说吧,笔试不是竞赛,不是为了拿满分,而是为了在有限时间内证明你具备基本的数据处理能力。一道能跑通的简单题远比一道写了一半的难题有价值。
4. 几类高频SQL题的破题思路:从听懂到能写对
SQL题考来考去,翻来覆去就是那几类经典题型。这里我挑了几个高频方向,把核心思路完整拆解一遍,你如果能把这些题型的代码熟练掌握,笔试的SQL部分基本稳了。
4.1 窗口函数:不只是会写,要理解它的执行逻辑
窗口函数是数据工程岗SQL题的核心工具。很多看起来复杂的SQL问题,一旦想到用窗口函数,解法就变得非常简洁。
窗口函数的执行顺序要牢记:先WHERE过滤,再GROUP BY分组,然后窗口函数计算,最后ORDER BY排序。注意WHERE在窗口函数之前执行,所以你不能在WHERE里直接使用窗口函数的别名。
以“统计每个用户听歌时长的累计值”为例:
SELECT user_id, song_id, play_ts, SUM(play_duration) OVER (PARTITION BY user_id ORDER BY play_ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum_duration FROM play_log;这里面有几个关键点:PARTITION BY表示按用户分组,ORDER BY表示组内排序,ROWS BETWEEN...AND...定义了窗口的范围。理解这三者的组合关系,你就能写出从累计求和到移动平均的各种查询。
4.2 连续问题:日期减去序号这一步是关键
“统计连续登录N天的用户”是SQL题里的常青树,它表面上考的是连续判断,实际考的是你能否用一个巧妙的转换把连续问题变成分组问题。
核心思路:(1) 先用窗口函数ROW_NUMBER()按用户分组、按日期排序,得到每个用户每个登录日期的序号;(2) 用登录日期减去序号得到一个日期值;(3) 如果日期是连续的,日期减序号的差值会保持不变,所以这个差值就可以作为分组标识。
WITH t AS ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS grp FROM login_log ) SELECT user_id, MIN(login_date) AS start_date, MAX(login_date) AS end_date, COUNT(*) AS consecutive_days FROM t GROUP BY user_id, grp HAVING COUNT(*) >= 3;这个技巧看着简单,但是假如你之前没接触过,考场上临时想是很费时间的。建议把它当成一个固定套路来记。
4.3 每日活跃/留存率:把握住“去重”和“时间偏移”两个核心
统计DAU(日活跃用户)的题目在笔试里非常常见,因为它直接对应业务里的核心指标。做题的时候要注意两点:一是去重逻辑(同一用户同一天产生多条记录应该只算一次),二是时间维度的处理(按天统计需要注意日期格式的转换)。
留存率的计算是另一个高频题型。以“次日留存率”为例,核心方法是把同一用户的登录记录按日期错位连接,找到第T天登录且第T+1天也登录的用户数。
SELECT a.login_date, COUNT(DISTINCT b.user_id) / COUNT(DISTINCT a.user_id) AS retention_rate FROM login_log a LEFT JOIN login_log b ON a.user_id = b.user_id AND b.login_date = DATE_ADD(a.login_date, INTERVAL 1 DAY) GROUP BY a.login_date;如果把DATE_ADD(a.login_date, INTERVAL 1 DAY)改成INTERVAL 7 DAY或INTERVAL 30 DAY,就能分别得到7日留存和30日留存。这类题目不复杂,但细节很关键,比如JOIN条件里必须限定日期偏移,否则会出现数据膨胀的问题。
4.4 时间区间重叠:峰值在线人数的经典解法
统计“在线用户峰值”或“会议室最大使用数量”这类问题,在数据工程笔试里被改编成“每分钟在线听歌人数峰值”出现的概率很高。这类题的核心解法非常巧妙:把每一条记录拆成两个事件,一个开始事件(+1),一个结束事件(-1),然后按时间顺序做累计求和。
WITH events AS ( SELECT user_id, start_time AS ts, 1 AS delta FROM play_log UNION ALL SELECT user_id, end_time AS ts, -1 AS delta FROM play_log ) SELECT ts, SUM(delta) OVER (ORDER BY ts) AS concurrent_cnt FROM events ORDER BY ts;然后再取出concurrent_cnt的最大值就是峰值。这个思路在很多真实场景里都能用到,比如并发任务数统计、直播间同时在线人数等。理解了事件流累加这个底层逻辑,这类题就都通了。
5. 踩坑记录和复盘方法:细节决定最终成绩
最后这部分我想聊聊那些容易被忽略、但实际考试中非常致命的细节。这些内容没有多少技术含量,可一旦踩中,很可能让你前功尽弃。
5.1 环境准备:笔试平台的隐藏坑
笔试用的在线OJ平台和本地IDE差别很大。第一个坑是代码自动补全的缺失。在本地IDE里写SQL习惯了自动提示表名和字段名,到了在线平台手写SQL经常会因为拼错字段名而报错。第二个坑是本地验证和在线评判的差异。平台一般提供本地自测用例,但通过自测不代表最终能拿满分,因为测试数据里会有一些你没考虑到的边界情况。
一个值得养成的习惯是:在正式笔试前,用牛客网或赛码网刷几套在线编程题,提前适应这些平台的输入输出格式和评判规则。尤其是输入输出解析,不同平台之间的规范可能不太一样,但一旦适应了,就不至于在考场上纠结“这行字符串到底要不要去掉末尾的回车”。
5.2 做题过程中的低级错误
SQL题最容易犯的低级错误包括JOIN条件漏写、GROUP BY字段不完整、COUNT和SUM的语义混淆、NULL值处理不当。这些错误在本地试数据量小的时候不容易暴露,一旦跑到全量数据上就会出现结果偏差。
编程题更容易栽在边界条件上,比如空列表输入、单元素列表、数字溢出等。写代码的时候可以给自己一分钟的时间,专门想想“如果输入是空的我这代码跑不跑得通”,这个习惯能帮你避免很多无谓的失分。
另外,读题一定要读完整,特别是题目最后几行对输出格式的要求。每年都有人因为输出格式不符合要求而丢掉一整道题的分数,格式问题最可惜。
5.3 考后一定要复盘
笔试结束不等于学习结束。无论这次笔试的结果如何,考后复盘都是提升能力最有效的方式。建议趁热打铁,把考场上没能做出来的题重新做一遍,对照题目回忆自己的思路卡在哪里,是知识点没掌握,还是时间分配出了问题。
复盘时可以把所有错题按题型归档,比如“SQL-连续问题”“SQL-留存率”“编程-区间重叠”“选择题-Flink”。春招期间要投的公司往往不止一家,腾讯音乐的笔试真题就是最好的练兵场,把每场笔试里的错题都变成自己的弹药库,下一场笔试的胜算才会明显提升。
根据我自己的经验,数据工程岗的笔试到最后拼的其实不是智商,而是你是否对常见题型足够熟悉。熟练度这东西,只能在反复练习中积累。把高频题型的套路吃透,再养成规范的做题习惯,拿到面试机会并没有想象中那么难。