尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

腾讯音乐数据工程笔试复盘:SQL窗口函数与数据倾斜成得分关键

腾讯音乐数据工程笔试复盘:SQL窗口函数与数据倾斜成得分关键 1. 笔试题全貌腾讯音乐数据工程岗到底在考什么第二批笔试的题目结构整体上延续了第一批的框架题型覆盖比较广大致包括单选多选选择题、SQL手写题、算法编程题和场景设计题。很多人刚开始看到这套卷子会有点懵因为它不是单纯考Java或者数据库而是把数据工程日常工作中真正会碰到的东西拆成了一个个考点。腾讯音乐的业务线包括QQ音乐、酷狗、酷我、全民K歌数据规模大场景覆盖了用户收听行为、商业化投放、内容社区互动和直播业务这就决定了笔试不会只停留在理论层面而是会结合真实业务场景来出题。我印象比较深的部分是SQL题和场景题的比例。整张卷子大约60%的分数落在SQL和大数据处理场景上25%落在算法题剩下的是计算机基础选择题。这个分布和很多纯后端岗位的笔试有明显区别。后端岗可能更看重Java基础和框架原理但数据工程岗显然更看重数据加工能力、数据建模能力和对数仓体系的理解。第二批笔试虽然题型稳定但题目在一些细节上做了调整主要是把更多权重放在了流批一体、数据质量这类偏实战的问题上。对于准备校招的同学来说这套题的核心价值在于它给你画了一个很清晰的能力边界你需要懂SQL懂Hive/Spark懂Kafka/Flink懂数仓分层同时还得有不错的代码功底。只刷LeetCode不够只背八股也不够。数据工程岗笔试的本质是在筛选“能独立完成数据链路设计和开发”的人而不是筛选“最会背概念”的人。1.1 能力模型距离业务最近的一类工程师数据工程岗在互联网公司的定位简单说就是负责把原始数据变成可用的数据资产。这中间涉及收集、清洗、加工、建模、调度、服务化一系列环节。腾讯音乐的业务数据来自多个端App埋点日志、服务端日志、数据库binlog、第三方合作数据这些数据的格式、时效性、质量都不一样。笔试中考到的很多内容本质上都是工作场景的简化版本。你可以把自己代入一个实际任务产品经理要求统计“本周每日收听时长Top10歌曲”。这一步看起来简单真正处理起来涉及多个问题。埋点日志里可能没有直接给出“歌曲收听时长”只有开始播放和暂停/结束事件需要拼接行为序列同一个用户可能会多次切歌、拖动进度条需要定义清楚什么是一次有效收听数据到达会有延迟实时统计和离线统计的口径还不一样。笔试中的SQL题和场景题就是围绕这类问题展开的。所以笔试中对SQL和多层嵌套子查询的考察不是单纯考语法而是在考察你能否把一个模糊的业务需求翻译成准确的数据加工逻辑。对Kafka、Flink的考察也不是让你背组件特性而是看你能不能设计一条实时链路在延迟和准确性之间做取舍。这就是数据工程岗笔试和普通技术岗笔试最大的区别它永远紧贴业务场景。1.2 第二批笔试的典型结构题量不小节奏要稳住从题型数量上看第二批笔试大致是15到20道选择题2道SQL手写题2道场景/简答题1到2道编程题。时间一般在90分钟左右。体感是选择题不能太纠结每道最多控制在2分钟以内否则后面的大题根本做不完。我认识好几个同学前面选择题做得很仔细结果后面SQL题和编程题时间不够只能草草写两句思路非常可惜。整体顺序上比较合理的做法是先快速浏览一遍所有题目把有把握的SQL题和场景题先做了因为这些题目一旦思路对了分数是确定的。编程题如果思路明确就先写如果一时没思路就先把暴力解写上去再考虑优化。选择题放到最后统一处理遇到不确定的先标记跳过不要恋战。第二批笔试还有一个特点就是平台会提供本地编译环境算法题支持多种语言提交。这个看起来没什么实际上对很多不熟悉在线笔试系统的人是一个需要提前适应的点。我在正式笔试前专门用牛客和赛码的模拟题练过几次主要是熟悉输入输出的处理方式。很多编程题本身算法不难但卡在输入格式上的人不在少数。2. SQL题目复盘窗口函数和多层聚合是得分命门如果让我只用一个词概括这批笔试SQL题的特点那就是“窗口函数密集”。两道SQL大题中几乎每一道都需要用到ROW_NUMBER、RANK、LAG、SUM OVER这类的窗口函数。第一题是用户连续播放行为分析第二题是歌曲维度的收听排行和留存分析。题目本身不算偏但嵌套层级多考察点很细。先看第一类常见考法连续播放天数。题目会给一张用户每日播放记录表字段大致包括user_id、dt、play_cnt。让你统计连续播放天数超过N天的用户数。这类题的标准解法是用日期减去行号得到一个分组标识然后按分组计数。核心逻辑是先用ROW_NUMBER按user_id分组按dt排序然后计算date_sub(dt, rn)作为flage最后group by user_id, flagehaving count大于等于N。-- 连续播放N天及以上用户统计 WITH t1 AS ( SELECT user_id, dt, ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY dt) AS rn FROM user_daily_play ), t2 AS ( SELECT user_id, dt, DATE_SUB(dt, INTERVAL rn DAY) AS flage FROM t1 ) SELECT user_id, MIN(dt) AS start_date, COUNT(*) AS continuous_days FROM t2 GROUP BY user_id, flage HAVING COUNT(*) 7;这个写法的好处在于一次分组就能把连续区间拆开不需要自连接性能相对可控。实际考试里有些同学会写成三层子查询加自连接逻辑上也能通但耗时和出错率都更高。我个人的经验是连续类问题基本都能用“日期减行号”这个思路解决值得练到肌肉记忆。第二类常见考法是TopN问题。比如统计“每个城市收听时长最长的前10位歌手”这就需要用到ROW_NUMBER加PARTITION BY组合。要注意的点是题目如果要求“第1到第10名”用DENSE_RANK可能和ROW_NUMBER结果不同尤其是存在并列名次时。阅卷时结果集是按照精确值来对账的所以读清楚“前10名是否允许并列”非常关键。-- 每个城市收听时长Top10歌手允许并列 SELECT city, singer_name, total_play_time, rk FROM ( SELECT city, singer_name, total_play_time, DENSE_RANK() OVER(PARTITION BY city ORDER BY total_play_time DESC) AS rk FROM city_singer_times ) t WHERE rk 10;表格对比一下几种窗口函数的差异会更清楚函数排序是否并列排名是否跳过适用场景ROW_NUMBER不并列强制唯一不跳过稳定TopN无并列场景RANK并列同号跳过竞赛排名并列后下一名跳号DENSE_RANK并列同号不跳过需要连续名次的前N名2.1 业务口径的考察比语法更让人头疼的地方SQL题真正拉开差距的地方不在语法而在业务口径的把握。这次笔试里有一道题要求统计“每个用户首次收听歌曲后的7日内复听率”这道题问法很简洁但至少包含了三个需要明确的隐含口径。第一个问题是“首次收听歌曲”怎么定义。是用户第一次听这首歌还是第一次在平台听任何歌如果是前者需要先找到每个用户每个歌曲的首次收听时间如果是后者逻辑就完全不同。题目原文用的是“首次收听歌曲”这个词没有明确副歌粒度但在数据工程里默认应该先按照“用户歌曲”维度找到首次时间因为“每首歌的7日复听率”和“全量听歌的7日留存率”是两个指标。第二个问题是“7日内”怎么按时间窗口计算。是按自然日比如首次收听当天算第0天还是往后推7*24小时这两个口径对结果影响很大。笔试中并不会提前告诉你正确答案阅卷也主要看你的SQL逻辑是否自洽、是否有清晰的注释。所以答题时把口径写清楚比闷头写一个大SQL更好。第三个问题是“复听”的判定条件。用户再次播放这首歌就算是复听还是要播放时长达到一定阈值才算这两个口径在业务上差别很大。很多候选人在这个地方没有做任何说明直接就按“再次出现记录即复听”来处理这样虽然也能跑通但暴露出你对业务指标定义不够敏感。我比较推荐的做法是先用注释写出假设再写SQL。比如“假设复听定义为用户再次点击播放且播放时长超过10秒”。这样即使结果和标准答案有偏差阅卷人也能看到你的思考过程。2.2 场景题埋点日志、数据倾斜和实时数仓腾讯音乐的笔试场景题部分考得比较综合。给一段类似真实埋点日志的JSON数据要求设计一套离线数仓分层方案并且回答如何处理数据倾斜问题。这种题目没有唯一答案但答得好不好差别非常大。先说数仓分层。如果考到音乐平台场景合理的分层是这样的。ODS层直接存放原始埋点日志保留所有字段分区方式按天存储格式建议用Parquet或者ORC。DWD层做清洗和规范化把JSON解析成结构化字段过滤无效数据统一时间格式明确事件类型。DWS层按业务主题汇总比如按“用户日维度”“歌曲日维度”“歌手周维度”沉淀宽表。ADS层面向具体报表和业务方需求比如“热门榜单”“用户收听时长分布”。我见过不少同学回答分层方案时把各层名称写了出来但对每层做什么处理没有任何细节。这种答案在阅卷人看来等于没写。每层需要说明输入是什么、输出什么、做了哪些具体加工逻辑。比如DWD层处理时需要解决埋点部分字段缺失问题比如没有经纬度就用默认值填充播放时长超上限做截断处理等。这些细节才是拉开差距的关键。再说数据倾斜。这是大数据处理中最常见的问题也是校招笔试的高频考点。经典场景是统计热门歌曲收听量时少数头部歌曲的数据量级比长尾歌曲高几个数量级group by这些热点key时所有数据都集中到少数几个reducer上导致Job卡在最后一个stage迟迟跑不完。常规的解决思路有几个方向。第一个是两阶段聚合先给key加随机前缀打散做一次部分聚合再去掉前缀做全局聚合适合group by场景。第二个是广播变量如果一个大表和一个小表维度表join把小表广播到每个executor上避免shuffle阶段的数据倾斜。第三个是调整并行度通过spark.sql.shuffle.partitions增加分区数来缓解单个task压力但实际上只能缓解不能根本解决问题。笔试中能把这三个方向都提到并且说明各自适用场景就已经是合格以上的水平了。3. 算法题思路TopK、二叉树和动态规划的老三样编程题整体难度在LeetCode中等偏下水平没有很偏很怪的题目。第一题是TopK问题第二题是二叉树相关第三道在不同题库里可能略有差异但基本围绕动态规划或者双指针。很多人担心数据工程岗是不是会考大数据量下的算法设计比如外排序或者bloom filter之类的实际上这批笔试没有考还是以常规算法为主。TopK题的经验是如果题目没有明确要求“不能修改原数组”或者“必须O(n)时间”用优先队列是最稳妥的解法。求最大K个元素用小顶堆维护堆的大小为K遍历完整个数组后堆里就是结果。时间复杂度O(n log K)空间复杂度O(K)。这个解法代码量小思路清晰不容易出错。public int[] topK(int[] nums, int k) { if (nums null || k 0) { return new int[0]; } PriorityQueueInteger heap new PriorityQueue(k); for (int num : nums) { if (heap.size() k) { heap.offer(num); } else if (num heap.peek()) { heap.poll(); heap.offer(num); } } int[] res new int[Math.min(k, heap.size())]; int idx 0; for (int val : heap) { res[idx] val; } return res; }这个解法有几个细节值得注意。堆的初始容量设置成k遍历时先判断堆是否满满了再比较当前元素和堆顶元素。如果当前元素比堆顶大就替换。这样整个过程中堆里始终保持当前最大的K个元素。回到笔试场景里题目如果是“找播放量最高的100首歌曲”小顶堆的思路可以直接迁移过去这也是为什么数据工程岗会考TopK的原因它和大数据场景中“在海量日志里找TopN”天然关联。二叉树题目比较常考的是层序遍历、最近公共祖先、二叉搜索树转双向链表。要是树的基础扎实这些都不算难。比较推荐提前把层序遍历的两种写法练熟DFS递归法和BFS队列法。笔试环境有时编译器语法提示不全平时练的时候不要过度依赖IDE自动补全手写能力很重要。动态规划如果遇到常见的还是背包、最长公共子序列、打家劫舍这类模板题。时间不够时先把状态定义和转移方程写出来即使代码没跑通阅卷人也能看到你的思路清晰。很多同学在编程题上一上来就追求最优解反而卡在边界条件上最后连暴力解都没写出来。我的建议是先写一个能跑通的解拿到基础分再考虑优化。3.1 编程题常见失误不是算法不会是输入输出和边界笔试做题和平常刷题有个很大区别就是没有用例提示也没有在线调试时那么细致的报错信息。最常见的问题出在输入解析上。有些题目要求从标准输入读取多行每行可能有多个整数用空格分隔。如果对split之后的空字符串处理不当很容易出现数组越界问题。尤其是读一行可能包含多个连续空格时用默认的split( )会留下空串应该用split(\s)。另一个常见问题是整数越界。TopK的题目里如果输入数据范围达到10^9级别用int存某些累计值时可能会溢出但笔试环境不一定有对应的测试用例覆盖。稳健的做法是涉及求和、乘法时优先考虑long除非题目明确说明不会溢出。二叉树题目中递归深度过大可能导致StackOverflow如果二叉树是链状的递归解法可能直接崩掉这时需要考虑迭代写法。关于时间和空间复杂度的标注建议在写完后用注释标注出来。阅卷人如果发现你的解法和标准答案不同但复杂度合理也会给对应的分数。不少候选人代码写得没问题但缺少复杂度分析给人一种“知其然不知其所以然”的感觉白白丢印象分。4. 大数据组件和计算机基础覆盖面广但有复习抓手选择题部分覆盖了Hadoop生态组件、数据库原理、操作系统、计算机网络、Java基础。说实话覆盖面非常广想靠考前突击全面覆盖不太现实但重点还是相当集中的。Hive和Spark相关题目基本围绕Hive SQL执行原理、Spark RDD和DataFrame的区别、shuffle过程、缓存级别、数据倾斜处理等展开。Kafka的题目集中在分区策略、消费者组、offset管理和消息可靠性保障。Flink的题目则围绕事件时间、处理时间、Watermark机制、Checkpoint如何实现精确一次语义。这些内容都是数据工程的“八股”虽然听起来背起来很枯燥但确实是日常工作里绕不开的基础。这里有一个复习技巧分享给大家。不要逐本啃书而是以“数据从产生到被使用”这条链路为线索把组件串起来复习。数据从客户端埋点产生进入Kafka消息队列实时链路用Flink消费加工离线链路用Hive或Spark定时处理结果写入数据库或者数仓服务层最终提供给报表或算法使用。沿着这条链路每个组件只需要吃透它的核心职责、关键机制和常见问题就能应对大多数选择题。数据库原理部分索引失效、事务隔离级别、三大范式、B树特性是高频考点。建议重点看MySQL的InnoDB存储引擎相关内容。操作系统和网络占比相对小一些重点看进程线程区别、死锁条件、TCP三次握手与四次挥手、HTTP和HTTPS区别。Java基础如果岗位描述没明确要求Java这几道选择题一般不会太难知道HashMap的底层结构和线程安全问题基本够了。组件选型类题目值得单独说一下。比如题目给一个“实时看板需求延迟要求5秒内数据量千万级每秒”场景问选择什么方案。标准答案是Kafka加Flink写入ClickHouse而不是用Spark Streaming写MySQL。这类题考察的不只是你知不知道某个组件还考察你能否根据延迟、吞吐、查询模式做合理选型。复习时建议多问自己一个问题这个组件解决了什么问题它的边界在哪里。4.1 数据模型和存储从OLTP到OLAP的转换思维数据工程的另一个考察方向是数据建模和存储选型。有一道题是问“业务库的订单表有10亿条记录现在需要做多维分析应该怎么做”选项里有加索引、分库分表、同步到数仓按天分区、直接对全表做聚合。很多人会选加索引但这里考察的是OLTP和OLAP的核心区别。业务库的索引是为点查设计的在10亿行上做多维分组聚合即使有索引也很慢因为要走全表扫描正确做法是同步到数仓建立按天的分区表按需扫描对应分区的数据。这类题目在选择题里出现频次很高考得都是“选型思维”。你不需要理解每个存储引擎的源码但需要清楚什么场景用什么组件。ClickHouse适合分析型查询Redis适合缓存和实时计数Elasticsearch适合全文检索MySQL适合事务型业务。一旦场景和组件错位性能就会断崖式下跌。还有一个高频考点是Spark中RDD、DataFrame、Dataset三者的区别。DataFrame相比RDD多了Schema信息Spark可以基于Schema做优化比如谓词下推和列剪枝。RDD虽然灵活但缺少结构信息优化空间有限。这个知识点经常以选择题形式出现选项会混淆“DataFrame是编译时类型安全”这种错误说法复习时要特别注意。5. 从笔试到拿Offer时间线复习路线与避坑清单如果你现在刚开始准备数据工程岗距离笔试还有一到两个月建议按三个阶段推进复习。第一阶段是SQL专项大约两周。目标是把窗口函数、多层子查询、Join的各种用法练熟。可以集中刷LeetCode数据库题目重点做中等难度以上的分组排序题。这里我特别推荐一个练习方法自己做一遍然后把答案里涉及窗口函数的实现背下来并默写直到形成条件反射。SQL题在笔试中是最好拿分的板块值得花这些时间。第二阶段是大数据组件和计算机基础大约一周到十天。不要追求把所有组件都钻透重点抓Hive、Spark、Flink、Kafka四件套的核心机制。可以用思维导图的方式整理考点每复习一个组件问自己三个问题它解决什么问题核心流程是什么有哪些常见坑回答得出来就说明基本掌握了。第三阶段是模拟笔试大约一周。重点做两件事第一件事是控制时间严格按照90分钟模拟整套题训练时间分配的手感第二件事是复盘错误对每道错题追究“为什么错”是知识点不会还是审题不清。模拟的另一个价值是适应在线笔试系统很多人第一次用牛客或赛码时发现代码编辑器没有自动补全切语言还要重新配置环境非常影响发挥。关于复习资料数据库方向可以看《SQL必知必会》加牛客SQL题库Hive和Spark方向可以看《Hive编程指南》的查询部分和《Spark快速大数据分析》的核心章节Kafka和Flink方向不太建议直接啃《Kafka权威指南》和《Flink原理与实践》这类大部头时间不够的话看技术博客和官方文档的精华部分就够用。避坑清单整理一下供大家对照参考序号坑点建议1选择题死磕卡时间不确定的先标记跳过最后统一处理2SQL不写注释和假设用注释写明业务口径和前提条件3编程题只写核心函数补全类的导入、主函数输入输出4输入解析踩多人空格坑用切分正则留意空字符串5忽略平台编译环境不熟提前用牛客/赛码模拟练习6场景题只堆名词每层/每个组件写清楚输入输出和操作7多选少选犹豫不确定时倾向不选按规则给分时更划算8考后不复盘每周至少完整复盘一次错题5.1 笔试当天的时间分配和心态管理笔试时间有限如何分配直接决定上限。我个人按“40%时间给SQL和场景题30%给编程题20%给选择题10%检查”的比例来安排。先做SQL和场景题因为这部分对思路的确定性要求最高做完后踏实感会很强。编程题如果第一题在15分钟内没思路果断先做第二题不要恋战。选择题有很强的“考记忆”属性放在后面心态紧张时反而更容易凭直觉选出正确答案。另外一个很实用的技巧是场景题和SQL题在答题框中尽可能结构化输出。分点列数字说明假设和处理步骤代码里加注释阅卷体验会好很多。在线笔试很多题是人工阅卷清晰的排版和注释能直接影响印象分这不算投机取巧而是让别人更容易看到你的逻辑。心态方面遇到没见过的题目时先判断它考的是哪个知识点再用“我知道的关于这个知识点的一切”来组织答案。比如遇到“如何保证实时计算不丢数据”可以想到Kafka的ack机制、Flink的Checkpoint、消费端手动提交offset至少能写出三个角度。校招笔试从来不是要求完美满分而是看你在有限时间内能不能稳定输出。6. 复盘与长期积累笔试之后真正的修炼才开始批改完这批笔试卷子我最大的感受是笔试题目其实在替行业筛选合适的人。数据工程岗位的日常工作就是跟“大规模数据的清洗、加工、建模、调度、质量保障”打交道这些能力不是考前一周能突击出来的。选择、题型、场景设计都是训练方法和思考习惯的体现。SQL写不好不代表不能入门但说明需要加大日常练习量场景题思路模糊不代表不适合这个岗位而是提醒自己平时做项目时多问几个“为什么这样设计”。我自己平时做项目时有个习惯只做数据开发还不够会刻意去想每条链路的边界情况。如果数据延迟怎么办如果上游字段变化怎么办如果任务失败重跑会不会产生重复数据。这种“没事找事”的思考方式后来验证是应对笔试题最有效的底层能力。笔试只是一个节点真正的修炼在于你对数据质量的敏感度和对业务口径的把控力。如果你现在还在准备阶段我的建议是两件事并行推进坚持刷LeetCode和SQL题保持手感同时找一个实际数据集从原始的埋点日志开始自己搭一套离线数仓从清洗到建模到指标计算完整走一遍。这个过程中遇到的问题会比刷题暴露出更多真实薄弱点。把这些问题逐个记录下来并解决到笔试时会发现很多场景题其实都来自你踩过的坑。
返回列表