
1. 这套笔试卷到底在考什么每年秋招季B站的数据开发岗位笔试题都会被很多人拿出来反复刷。2020年这套卷子已经算是这类岗位笔试里的一个典型样本了即便放到现在来看它的考点分布和出题思路依然有很强的参考价值。我陆陆续续带过几个实习生也帮他们修改过简历和模拟面试普遍反馈是这套卷子不像部分互联网大厂那样偏向纯算法题而是更看重你对数据开发整个链条的理解是否完整。数据开发方向和其他技术岗有一个本质区别它不是一个单纯的“编程岗位”而是横跨数据采集、数据清洗、数据建模、数据计算、数据服务的综合性岗位。笔试如果只靠背SQL语法或者只靠刷LeetCode很容易在某几个特定模块上翻车。B站的这套笔试卷二基本就把这个问题暴露得很彻底。先给一个整体定位。这套笔试卷的题型大致可以分为四类题型类别考查方向题目占比SQL编写与优化数据提取、聚合计算、窗口函数、join优化约35%大数据组件原理HDFS、MapReduce、Spark、Hive等核心机制约25%数据仓库建模维度建模、分层架构、事实表和维度表设计约20%算法与数据结构常见算法题、离线计算/实时计算中涉及的算法思路约20%为什么是这个分布因为B站的数据开发团队日常处理的核心场景就是内容生态分析视频投稿、播放、弹幕、评论、用户增长分析注册、留存、活跃、商业化分析广告投放效果、营收归因。这些场景对应的技术栈实际上非常稳定笔试出题自然也会往这些方向去靠。所以如果你是准备投递B站数据开发岗位的应届生或者刚转行想进入大数据领域这套卷子实际上是很好的“体检报告”。它不只是在测试你会不会写代码而是在测试你有没有真正理解一个数据平台是怎么运转的。接下来我会按照这套卷子的核心知识点逐块拆解顺带把对应的准备方法和易错点一起讲清楚。2. SQL题目看似送分实则全是坑2.1 窗口函数是绝对重点B站这套笔试卷里SQL题占比最高而窗口函数又是SQL题里的高频考点。基本可以确定的是凡是考SQL必然会有一道题和排名、分组TopN、连续登录天数有关。举个例子试卷里非常容易出现类似这样的题目有一张用户视频观看记录表 watch_log字段包括user_id用户ID、video_id视频ID、watch_date观看日期、watch_duration观看时长单位秒。请统计每个用户观看时长最长的前3个视频以及对应的排名。这题如果不会窗口函数硬用group by去做逻辑会绕一大圈。正确解法是用row_number()或rank()select user_id, video_id, watch_duration, rn from ( select user_id, video_id, watch_duration, row_number() over (partition by user_id order by watch_duration desc) as rn from watch_log ) t where rn 3;这里面有一个很容易被忽略的点row_number()和rank()的区别。row_number()遇到相同值会随机分配序号比如两个视频观看时长一样一个排第1一个排第2rank()则会并列排名比如两个视频并列第1那下一个视频的排名就是第3。具体选哪个要看业务诉求是“严格取前3条记录”还是“取排名前3的所有记录”。这个是很多人在笔试里丢分的地方不是不会写而是没想清楚业务含义。2.2 连续登录问题的两种解法连续登录天数是另一道高分值SQL题。它的核心逻辑是把用户登录日期和用户的“分组序号”相减如果日期是连续的相减得到的日期应该是不变的。select user_id, min(login_date) as start_date, max(login_date) as end_date, count(*) as continuous_days from ( select user_id, login_date, date_sub(login_date, row_number() over (partition by user_id order by login_date)) as group_id from user_login_log ) t group by user_id, group_id;如果数据量特别大比如一张登录表里面有上亿条记录那这个方案会在date_sub计算和分组聚合上消耗较多资源。实际生产环境中更推荐用按天去重后再计算的思路先把同一用户一天内多次登录去重再套上面的连续分组逻辑。笔试题目里如果没说明默认一天只记一条但实际工作中这种细节必须自己考虑到。2.3 JOIN优化容易被忽视这张笔试卷里还有一道比较典型的JOIN优化题考点很直接大表JOIN小表怎么优化很多人的第一反应是“给关联字段加索引”。这在传统关系型数据库里没错但在Hive/Spark里完全不适用因为底层是分布式计算不走索引机制。正确的优化方向应该是使用MapJoin把小表加载到每个Mapper节点的内存里避免Shuffle合理设置spark.sql.autoBroadcastJoinThreshold让Spark自动识别小表并广播如果两个表都特别大就需要考虑分桶Join或Skew Join举一个实际场景B站做视频推荐分析时经常需要把“几十亿条用户行为日志”和“几百个视频分类维度表”做关联。这种场景如果不走MapJoin整个任务跑几个小时都不奇怪走MapJoin之后基本几分钟就能搞定。笔试里遇到这类问题不要只答一个“用小表驱动大表”要把底层原理讲透让面试官看到你不是在背答案而是真的理解分布式计算的执行过程。3. 大数据组件原理不能只停留在“会用”3.1 HDFS读写流程必须要能画出来大数据组件原理这部分的考点非常固定HDFS的读写流程基本是必考。这套卷子里也少不了。为什么总考这个因为HDFS是几乎所有离线大数据平台的存储底座如果连它的工作原理都不清楚后面根本没法排查问题。我面试候选人的时候最怕听到的回答是“我平时就是hadoop fs -put上传文件”这个回答在笔试里也过不去。HDFS写入流程的核心步骤是客户端向NameNode发起写请求NameNode检查权限、目录是否存在并返回可用的DataNode列表客户端将文件分成Block默认128MB按顺序写入第一个DataNode第一个DataNode收到数据后复制给第二个DataNode第二个复制给第三个形成流水线复制所有副本写完后客户端通知NameNode写入完成这里面常考的细节是副本放置策略。默认策略是第一个副本放在客户端所在的节点如果客户端不在集群内则随机选一个节点第二个副本放在与第一个副本不同机架的节点第三个副本放在与第二个副本相同机架的另一个节点。这个策略的核心目的是在“容灾”和“写入性能”之间取平衡既要保证数据不会因为整个机架断电而全部丢失又要尽量避免跨机架复制带来的带宽消耗。3.2 Spark的Shuffle机制理解它就理解了Spark性能Spark相关的考点里Shuffle是绝对绕不开的。B站这套笔试卷里有一道题问的是Spark作业运行缓慢的排查思路核心指向就是Shuffle。Shuffle简单理解就是数据在多个节点之间重新分配的过程。比如group by key相同的key必须被分到同一个节点上去处理这就需要把各个节点上的数据打乱重排。Spark的Shuffle过程有几个关键词需要理解Shuffle Write每个Mapper任务把结果写到本地磁盘按照key进行分区Shuffle Read每个Reducer任务从所有Mapper节点拉取属于自己的那部分数据Shuffle Spill内存不够时把数据溢写到磁盘会有额外的序列化和IO开销实际操作中Shuffle相关的调优手段很多比如调整spark.shuffle.file.buffer默认32KB调高可以减少磁盘IO次数、开启spark.shuffle.consolidateFiles合并小文件、设置合理的分区数等。但笔试里更重要的是能够说清楚为什么Shuffle是Spark作业性能瓶颈的高发区——因为Shuffle涉及磁盘IO、网络传输、序列化等多个环节任何一个环节出现短板整个作业都会被拖慢。3.3 Hive与Spark SQL的差别不能搞混试卷里有一类题喜欢“钓鱼”给你一段Hive SQL问它在Spark SQL上跑需要注意什么。这个考点很实际因为很多团队在做离线数仓时底层的计算引擎已经从Hive on MR切换到了Spark SQL但SQL语法和调优思维却没有完全跟上。最大的差异在于执行模型。Hive on MapReduce把每个SQL操作拆分成一个或多个MapReduce任务每一步的结果都写入HDFS容错性好但是慢Spark SQL则会在内存中构建DAG有向无环图尽量把多个操作串联起来减少落盘次数。这导致一个常见问题同样的SQL在Hive上跑得很好拿到Spark SQL上反而报内存溢出。原因往往是Hive里SQL产生了大量中间结果这些结果在Hive里是落盘到HDFS的不会占用执行内存但在Spark里如果Join或GroupBy的数据量超过了executor内存就会直接OOM。解决思路是增加分区数或者在合适的场景下使用broadcast join把大表Join小表的Shuffle直接省掉。4. 数据仓库建模这是区分“SQL Boy/Coder”和“数据开发工程师”的分水岭4.1 为什么要分层B站这套笔试卷中数据仓库建模的题目占比不算特别高但分值很重。有一道题直接问“你们理解的数据仓库分层结构是什么每一层的作用是什么”这道题看似简单很多人也能背出“ODS、DWD、DWS、ADS”这些缩写但真正能把每层的作用和设计原则讲清楚的候选人比例很低。我打个比方数据仓库分层就像是厨房的流水线ODS层原始数据层相当于“买菜回来不做任何加工”生肉是生肉蔬菜是蔬菜原样放进冰箱。这一层的数据和源系统保持一致用于追溯和数据备份DWD层明细数据层相当于“洗菜切菜”把数据做清洗、去重、标准化产出干净的、可复用的明细数据DWS层汇总数据层相当于“配菜”按照业务维度把常用指标提前聚合好比如按天、按用户维度统计好的PV/UV等查询的时候不用重新跑明细ADS层应用数据层相当于“上桌的菜”直接服务于具体报表或业务产品的数据这个分层结构对应的核心思想就是**每一层解决一类问题避免数据关系像蜘蛛网一样纠缠不清。**如果没有分层所有需求都直接查ODS原始数据那么每条业务线都要自己解析日志、自己清洗、自己计算指标。一旦口径变了所有下游应用全部要改维护成本极高。4.2 维度建模中的退化维度陷阱这套卷子里有一道比较有区分度的题目关于维度建模中“退化维度”的处理。退化维度Degenerate Dimension指的是原本应该存在于维度表中的维度字段直接放在了事实表中不再单独建维度表。典型的例子就是订单表中的“订单号”字段。订单号既是一个事实每个订单产生一次又包含了维度属性订单状态、下单渠道、支付方式等但它只跟当前这个订单一一对应拆成一张独立的订单维度表反而会导致无谓的JOIN。这里真正容易犯错的地方在于很多初学者分不清哪些字段该退化为事实表的属性哪些该保留为外键关联维度表。一个比较实用的判断标准是看字段的“基数”和“复用性”订单号、交易流水号基数极大但每次查询都是单独关联适合退化到事实表商品ID、用户ID基数也很大但这边的商品名称、用户等级等属性被多个业务场景复用应该独立建维度表支付渠道、订单状态这类枚举值可以退化到事实表直接存code通过字典表解释即可这样设计的核心逻辑是让事实表尽量“胖”减少查询时需要JOIN的维表数量从而提升查询性能让维度表尽量稳定集中管理可复用的描述性属性。4.3 指标口径的一致性怎么保证B站这套卷子的数仓建模部分还涉及一个特别实际的问题同一个指标在不同报表里口径不一致怎么办这个问题本质上就是数仓领域的“指标治理”问题。举个例子视频的“播放量”这个指标A部门定义的是“用户点击播放按钮并至少播放3秒”B部门定义的是“视频加载完成即算一次播放”。两边统计口径不一样出来的数字自然对不上业务方就会困惑到底该信哪个。解决方案一般有三种层级在DWD层统一清洗逻辑定义好“有效播放”的边界在DWS层建设统一的指标字典将指标名称、计算公式、统计粒度、更新频率全部登记在册在ADS层做数据校验确保同一指标在不同报表中输出一致笔试或者面试中答这类题重点不是罗列概念而是要体现出“这个问题我实际遇到过并且有对应的解决办法”。5. 算法题并不只是LeetCode而是数据开发的日常缩影5.1 TopN问题的多种实现方式B站这套笔试卷中算法题目的数量和难度都控制在一个合理的范围内不会像算法岗那样考困难题但基础的数据结构和算法思维是必考的。其中TopN问题几乎是每个数据开发岗位笔试的标配。TopN问题在业务里非常常见播放量Top100的视频、涨粉最快的UP主、弹幕热度Top50等。笔试里面可能直接让你写代码也可能把它包装成一个情景题。如果只是单机的数据量一个堆排序就能解决问题import heapq def top_n(nums, n): return heapq.nlargest(n, nums)但如果是海量数据比如几十亿条播放记录要统计Top100的视频单机的思路就行不通了。这时就需要用分治法先对数据进行分片每片分别计算TopN最后再合并各片的TopN结果。Spark里做这件事就更方便了直接用orderBylimit底层框架会自动优化执行计划。但笔试里的算法题更想看到的是你能不能在不依赖框架的情况下用基础的数据结构和算法把思路表达清楚。5.2 海量数据处理的高频套路数据开发笔试里的算法题很多时候并不是纯粹的算法题而是“海量数据处理题”。这类题的核心套路其实就那么几类哈希分治把大文件通过哈希取模拆成多个小文件每个小文件单独处理位图法判断一个数是否在某个集合里比如统计UV时给每个用户分配一个偏移量布隆过滤器判断某个元素“一定不存在”或者“可能存在”在大规模去重场景下能节省大量内存Trie树处理字符串前缀匹配的场景比如敏感词过滤、搜索词推荐举个具体的场景假设B站一天有2亿条弹幕数据需要过滤掉重复的弹幕内容。如果直接用一个HashSet去重内存可能会爆掉。如果用布隆过滤器初始化一个适当大小的位数组每来一条弹幕就计算多个哈希函数并映射到位数组中的位置如果所有位都被置为1说明这条弹幕可能在之前出现过只要有一个位是0就一定没出现过。整个过程内存占用只有HashSet的几十分之一。笔试里能够把这些思路表达出来同时说清楚它们的优缺点比如布隆过滤器有误判率不能删除元素就比死记硬背强太多了。6. 业务场景题B站特色考题背后的数据思维6.1 如何统计视频的“有效播放”B站这套笔试卷里最让我印象深刻的是一道业务场景题如何定义和统计一个视频的“有效播放”。这个题目很有B站特色也同样出现在很多内容平台的数据开发笔试中。它考察的不是你能不能写SQL而是你怎么把一个模糊的业务概念转化成清晰的技术方案。“有效播放”的定义并不是唯一的需要产品、运营和技术一起协商确定。常见的有两种口径播放时长阈值法播放时长超过一定秒数比如3秒、10秒、30秒才算有效播放播放进度比例法播放进度超过视频总时长的某个百分比比如5%才算有效播放两种口径各有优劣。时长阈值法实现简单但对长视频和短视频的公平性不足——一个10分钟的视频刷了10秒可能还没进入正片一个15秒的短视频刷10秒就已经看了一大半。进度比例法则更贴近“用户真的在看”这个语义但需要join视频信息表计算逻辑更复杂一些。实际实现中很多团队会在上报日志里同时记录video_id、play_start_time、play_duration、video_total_duration。之后在DWD层做清洗时根据约定的口径统一打标is_valid_play下游所有应用只需要基于这个标签做聚合即可。这里就涉及到前面讲到的指标口径一致性——如果没有在DWD层统一打标每个部门自己算自己的数据一定对不齐。6.2 实时计算场景的选型思路B站这套笔试卷里还有一道实时计算相关的应用题问的是直播场景下如何实时统计在线人数这题对没有接触过实时计算的同学来说会有点懵。因为在线人数的统计和普通PV/UV不一样它天然带有“时间窗口”属性而且有进出两个动作。常见的实现方案有两种方案一基于Flink的滚动窗口聚合直播间的进房、退房行为都会产生事件流每来一个事件就更新当前房间人数然后按固定时间窗口比如每5秒输出一次当前在线人数DataStreamLiveEvent stream env.addSource(new FlinkKafkaConsumer(live_event, ...)); stream .keyBy(event - event.getRoomId()) .window(TumblingProcessingTimeWindows.of(Time.seconds(5))) .aggregate(new CountAggregate()) .addSink(...);这个方案实现简单但窗口边界会有误差。假设某用户在10:00:03进入直播间10:00:07退出这两个事件可能落在两个不同的窗口里。结果是A窗口加了1B窗口减了1但A窗口统计时实际用户已经不在线了。方案二维护实时状态 定时输出使用Flink的KeyedProcessFunction为每个直播间维护一个当前在线人数的状态值。进房事件到达时状态加1退房事件到达时状态减1同时注册一个定时器每隔5秒把当前状态值输出一次。这个方案更精准但需要处理状态过期、事件乱序等问题实现复杂度更高。笔试里能把这个场景的两种方案都说清楚并且说明各自的优缺点面试官基本就能判断出你是有真实项目经验的。这也是为什么这套卷子被很多人称之为“有水平”——它不是死记硬背就能通过的考试而是真的在考察数据开发的核心思维。7. 备考建议如何高效准备这类数据开发笔试卷7.1 知识点优先级排序结合B站这套笔试卷二的考点分布我建议准备方向按以下优先级来SQL窗口函数、分组聚合、JOIN每天至少写3道题保持手感Hive/Spark原理执行流程、Shuffle机制、常见报错排查数仓建模分层架构、维度建模、事实表和维度表的区分业务场景题多总结内容平台、电商平台常见的分析口径算法与数据结构海量数据处理套路为主LeetCode中等难度为辅7.2 刷题之外的准备工作除了刷题还有一个很多人会忽略的准备在简历中梳理至少一个完整的数据项目。笔试之后紧接着就是面试如果笔试分数不错但简历上的项目一问三不知那基本就止步于此了。一个能够拿出来讲的项目至少要包含这些环节项目背景解决什么业务问题数据来源埋点日志、业务库同步还是第三方数据技术架构用到了哪些组件、为什么这么选型建模过程事实表和维度表怎么设计的指标口径怎么定的性能优化遇到过什么性能问题、怎么解决的最终效果给业务带来了什么价值最好有量化数据我在之前的文章里也反复强调过数据开发这个岗位最看重的是“能不能把业务问题翻译成技术方案”。这套笔试卷的每一道题本质上都是在做这种翻译工作。7.3 保持好奇心和总结习惯回到这套2020年的笔试卷现在回头看有些技术选型可能已经过时了比如Hive on MapReduce在越来越多的场景下被Spark SQL替代实时计算也从当年的Storm、Flink并存变成Flink基本一统天下。但这套卷子背后考察的数据开发核心能力——SQL功底、组件原理理解、数仓建模思维、业务理解能力——从来都没有变过。我的经验是准备这类笔试不要只把它当成求职的敲门砖而要把每个知识点都理解透。B站这套卷子之所以值得反复研究就是因为它能让认真准备的人构建出一套完整的数据开发知识体系这套体系会在你整个职业生涯中持续发挥作用。8. 常见问题和避坑经验8.1 SQL题最容易被扣分的三个细节SQL题是B站这类笔试卷的得分主力但很多人在非技术细节上丢分非常可惜。第一没有处理NULL值。统计一个用户的总观看时长时如果某条记录的时间字段是NULLsum()函数会直接忽略它但count()函数会把NULL也计数进去。业务上如果希望NULL按0处理就要用nvl()或coalesce()显式转换。第二去重逻辑不严谨。统计UV时忘了用count(distinct user_id)或者用了去重但没搞清楚distinct放在count()里和放在查询列表里的区别。第三没有考虑数据倾斜。试卷里的SQL题虽然不会真的给你跑一个巨大的集群但如果你能在答案里主动指出“这个SQL在真实数据量下可能导致某个key数据倾斜可以这样优化”会大大加分。8.2 大数据组件题目不要死记硬背很多人在准备大数据组件原理时喜欢背一些概念比如“NameNode负责元数据管理”“DataNode负责数据存储”。这些当然没有错但笔试的题目通常不会停留在这种层面而是会问“如果某个DataNode节点宕机了HDFS会发生什么”“NameNode重启的时候大量DataNode同时上报Block报告怎么办”“Spark作业频繁Full GC可能是什么原因”这些问题考察的是在真实环境下你对这些组件运行机制的理解程度。建议在准备时多问自己几个“如果……会怎样”。如果有条件的话搭个单机伪分布式环境自己把NameNode停掉观察DataNode的行为比背十遍文档都管用。8.3 业务场景题先给结论再展开论证业务场景题是最容易拉开分差的题型。因为这类题没有标准答案考察的是分析思路和表达能力。我建议采用“总-分-总”的结构来回答先给出明确的处理思路第一句话就说清楚用数据比如“一个10分钟的视频用户看了8秒”来举例按步骤拆解技术方案最后做一个小结说明这个方案的优势和可能的潜在问题这一套下来不仅逻辑清晰还给后续的面试提问留了更多讨论空间不会一下把话说死。8.4 关于2020年这套卷子的最后一点说明这两年陆续带着新人复盘过几遍这套B站笔试卷二每次都能发现一些新的收获。笔试卷在变、技术在迭代但数据开发这个岗位的本质没有变它永远需要你在业务理解和技术实现之间找到那个最合适的平衡点。如果说有什么备考心得值得分享那一定是把每一个题目背后对应的业务场景想清楚所有的技术都只是实现手段而已。