
1. 一份校招笔试题背后的考察逻辑数据开发到底在考什么最近翻到一份京东2019校招数据开发工程师笔试题虽然时间过去几年但我认真看了一遍之后发现这类题目放到今天依然是数据开发、大数据开发面试题里最经典的底料。很多正在准备校招的同学问我要面经我通常都会让他们先把这种老题吃透原因很简单它考的不是某个框架的冷门API而是数据开发这个岗位最底层的几个能力——语言功底、组件原理、SQL功底和工程思维。先说一个我自己的判断数据开发笔试本质上不是技术问答赛而是一道海选过滤器。面试官心里很清楚校招生大多没有大规模集群实战经验他们要筛的不是谁用过哪些公司内部系统而是谁的基础够扎实、谁遇到问题时有完整的分析链路。从历年真题来看数据开发笔试题型的分布其实蛮固定大致是这么几块考察模块常见题型考察目标Java/Scala语言与JVM选择题、简答题工程落地能力能不能写生产代码Hadoop生态组件原理选择、填空、简答是否真正理解大数据组件的工作机制Hive与数仓建模简答、SQL手写数据开发核心生产力Spark/flink实时计算简答、方案设计实时链路理解程度SQL场景题与算法题手写SQL、代码题逻辑思维和编码基本功这份考察框架放到今天的校招依旧有效。很多同学复习时容易陷入一个误区整天刷LeetCode或者抱着某本书背组件概念结果笔试时发现题型其实是交叉的——一道SQL题里藏着数据倾斜的考点一道Java题里藏着JVM内存模型和Spark内存管理的关联。所以下面的拆解我会把题目背后真正想考的东西也一并讲出来而不是单纯给答案。2. 语言与JVM基础看似送分、实则区分度最高的部分2.1 HashMap的考点为什么永远绕不开数据开发岗的笔试Java基础里出现频率最高的题HashMap绝对排前三。2019年京东的这份题里就有关于HashMap原理的选择题后来我在很多大厂的笔试题里也反复见到。它考的不是HashMap怎么用而是这几个点put操作的完整流程、扩容机制、1.7和1.8的差异、为什么线程不安全。我建议大家按这个顺序去梳理put时先计算key的hash值然后通过扰动函数混合高位信息再对数组长度取模定位到桶如果桶为空直接放入如果桶非空判断是链表还是红黑树然后遍历查找key是否已存在存在则替换value不存在则插入新节点插入后检查size是否超过threshold capacity * loadFactor超过则触发resize数组翻倍数据重新分布。很多人会忽略一个细节JDK 1.7是头插法JDK 1.8改成了尾插法。为什么要改因为头插法在并发扩容时会形成环形链表导致下一次get时死循环。而1.8改成尾插法后并发场景下虽然还会丢数据但至少不会死循环了。这个细节几乎年年考而且经常以下列哪个说法正确的形式出现。你要是问我HashMap怎么准备我的建议是不要背拿一张纸画一遍1.8的put流程从数组到尾结点到红黑树化链表长度大于8且数组长度大于64全部画出来能画出来就说明真懂了。2.2 JVM内存模型与GC笔试题里的固定嘉宾JVM相关考点通常包括内存区域划分、对象创建过程、垃圾回收算法、常见垃圾收集器、类加载机制。笔试常考的是前两个。内存区域要分清楚线程共享和线程私有。堆和方法区是线程共享的虚拟机栈、本地方法栈、程序计数器是线程私有的。然后要搞懂对象在堆里的分配过程先尝试在栈上分配如果对象体积小且没有逃逸栈上分配失败则进入堆优先在TLABThread Local Allocation Buffer中分配这就是为什么每个线程在堆里都有一块私有缓冲区TLAB空间不足则直接在Eden区分配Eden区不够时触发Minor GC存活对象进入Survivor区。这里特别提醒数据开发工程师每天跟Spark打交道而Spark的Executor内存管理里也有类似的堆内/堆外概念很多面试官会顺着JVM提问往Spark内存上引导所以这块不能只背八股要能串起来讲。GC部分最少要能说清楚对象存活判断可达性分析三种基本回收算法标记清除、标记复制、标记整理新生代用复制、老年代用标记整理或标记清除这个主线。CMS和G1的区别也是高频题G1的优点在于Region化布局可以指定最大停顿时间并且能在回收过程中做增量处理。如果你能把G1的Region、SATB、Remembered Set讲出来基本就能镇住场面。2.3 Scala考点伴生对象、case class与隐式转换数据开发用Scala写Spark是常态所以笔试里偶尔会出现Scala基础。重点就几个val和var的区别immutable和mutable集合的默认选择伴生对象和伴生类的关系apply方法的作用case class和普通class的区别为什么Spark中定义schema经常用case class隐式转换和隐式参数。有几个同学问过我Spark里为什么推荐用case class而不是普通class答案不是更简洁这么简单。case class默认实现了equals、hashCode、toString并且支持模式匹配最关键的是它在序列化时比普通对象更友好——这在分布式计算里意义重大因为RDD/Dataset里的对象要跨节点传输序列化效率和正确性直接影响作业能否跑通。3. Hadoop核心机制HDFS读写与MapReduce死磕到底3.1 HDFS写流程讲清楚ack和副本放置策略才算出彩有一类题是这样的客户端向HDFS写入一个128MB的文件请描述完整过程。常规答案是客户端调用DistributedFileSystem.create然后向NameNode发起请求……但对校招来说能把下面几个细节写出来分数会明显不一样。第一个细节是ack机制。数据以packet为单位发送默认每个packet是64KB客户端把packet放入DataQueue队列DataStreamer从队列取出后发送给第一个DataNode第一个DataNode再传给第二个第二个传第三个。每写一个packet下游节点会向上游返回ack最终由最后一个DataNode把确认信息返回给客户端。如果某个DataNode写失败会把失败的节点从管线中移除然后继续写剩余副本最后NameNode会触发副本重平衡。第二个细节是副本放置策略。默认副本数是3第一个副本放在客户端所在节点如果客户端不在集群内则随机挑选一个节点第二个副本放在与第一个副本不同机架的节点上第三个副本放在与第二个副本相同机架的不同节点上。这个策略的用意是既能容忍一个机架故障因为至少有一个副本在别的机架又能减少跨机架的网络传输开销。第三个细节是网络拓扑距离。HDFS计算节点距离是沿着树找最近公共祖先两个节点距离越近读写性能越好。计算距离的公式要会写很多选择题会直接考这个东西不懂的很容易被绕进去。3.2 NameNode元数据管理fsimage、edits与checkpoint这个考点有点隐蔽但大厂笔试几乎必出。它问得最多的形式是edits文件和fsimage有什么区别SecondaryNameNode的工作流程是什么先理清机制NameNode把文件系统的元数据变更追加写入edits日志内存中维护一份完整的元数据镜像。fsimage是持久化的元数据快照但不包含最新变更最新变更都在edits里。SecondaryNameNode不是NameNode的热备它做的事情是定期把edits和fsimage合并成新的fsimage再推送回NameNode。合并过程是SecondaryNameNode通知NameNode生成新的edits.newSecondaryNameNode把fsimage和edits拉到自己本地在内存中合并生成新的fsimage.ckpt把fsimage.ckpt推回NameNode替换原fsimage并改名edits为edits.new对应的状态。触发checkpoint有两个条件fs.checkpoint.period默认3600秒fs.checkpoint.size默认1GB任一满足就会触发。这个考点之所以高频是因为它直接关系到一个生产问题NameNode如果宕机重启时需要加载fsimage和edits如果edits过大重启就会非常慢而checkpoint机制就是为了控制edits的大小。3.3 MapReduce Shuffle环形缓冲区、溢写与combinerMapReduce相关的笔试题十道里有七八道绕不开Shuffle。核心知识点就三个第一Map端的环形缓冲区。默认大小100MB溢写阈值为80%也就是说写到80MB就开始溢写而Map任务可以继续往剩余的20MB中写。为什么要设计成环形为了在持续写入和后台溢写两个动作之间做到无锁并行这在工程上是一个很有意思的设计。第二溢写时要做的几件事分区、排序、合并如果配置了combiner。分区默认使用HashPartitioner按key的哈希值对reducer数量取模。排序默认按key的字典序。这里有个常见的混淆点分区和排序不是一回事分区决定数据去哪排序决定数据在分区内的顺序。第三combiner会多次执行。它不仅会在Map端溢写时执行还会在多次溢写文件合并时执行。但一个重要原则是combiner不能改变最终结果所以不是所有函数都能做combiner。比如求平均值Map端combine如果把部分均值算出来最终Reduce端再求平均就会出错而求和或者求最大值就可以安全使用combiner。再补充一个容易被忽视的点MapReduce的排序发生在多个阶段——Map端溢写时按key排序Reduce端拉取数据后还要做归并排序。最终输出到reduce函数的数据是分区有序、区内按key有序的。这个结论在很多SQL场景题里也用得上比如Reduce端为什么能直接对key做group by就是因为有这一层排序保证。4. Hive与数仓建模数据开发的核心生产力考察4.1 Hive架构与SQL执行流程别只答SQL转MapReduceHive的题目一般从架构开始。Hive由MetaStore、Driver、Compiler、Optimizer、Executor构成。用户提交一条SQL后经过Parser语法解析、SemanticAnalyzer语义分析、LogicalPlanGenerator生成逻辑计划、Optimizer逻辑计划优化、PhysicalPlanGenerator生成物理计划最终转换成MapReduce或Spark或Tez任务执行。笔试题里最喜欢问的是一条Hive SQL从提交到返回结果经历了哪些环节很多同学只写转化成MapReduce这只能拿一半分。要写完整链路最好还把MetaStore的作用点出来表的schema、分区信息、SerDe、存储路径都在MetaStore里而HiveSQL编译时需要去MetaStore查元数据来校验字段和表是否存在。还有一个常考细节Hive on MR和Hive on Spark的区别。核心差别在于执行引擎不同前者每个MR任务都要启动和销毁JVM后者基于Spark的DAG可以复用Executor所以小作业上Spark引擎会快很多。这个点经常以选择题出现选项会写Spark引擎比MR快,你要知道快在哪里不能只记结论。4.2 内外部表、分区表、分桶表的正确打开方式Hive这部分考得比较基础但错误率不低。我见过太多人在内部表和外部表的区别上翻车。对比项内部表Managed Table外部表External Table数据是否归Hive管理是否DROP表时数据是否删除删除表结构数据只删除表结构数据保留在HDFS适用场景中间表、临时表原始数据、ods层共享数据记住一句话能选外部表就选外部表尤其是ODS层的原始数据万一误删表数据还能救回来。这个观点在面试中说出来会很加分因为面试官能看出你有生产环境的风险意识。分区表和分桶表的区别也是高频考点。分区表是目录级别的粗粒度切分分桶表是文件级别的细粒度切分。分区字段不是表内实际字段而是一个伪列分桶字段必须是表内实际字段且分桶数要根据数据量合理设置太小会导致单个文件过大太大则会产生大量小文件。4.3 数仓分层设计ODS、DWD、DWS、ADS到底各司其职数仓建模这块笔试通常以简答题或方案设计题出现请设计一套数据仓库的分层架构并说明每层的作用。标准答案结构如下ODS层数据原始层直接同步业务库数据结构保持不变用于数据备份和追溯DWD层明细数据层对ODS做清洗、去重、维度退化、标准化处理保持与业务过程一致的粒度DWS层汇总数据层按主题做轻度汇总如用户维度的日汇总、商品维度的日汇总ADS层应用数据层面向具体业务需求加工的数据如报表、大屏、推荐等。这里要解释清楚一个设计原则为什么要分层直接一条SQL从ODS算到ADS不行吗答案是可以但会带来几个问题——大量重复计算、口径不统一、业务变更时影响面太大。分层本质上是用空间换时间、用冗余换稳定每一层只对上层负责修改下层不影响最终应用。面试时能把这个逻辑讲明白比背一堆每层英文缩写强得多。4.4 数据倾斜的经典解法从SQL改写到底层机制数据倾斜是Hive笔面试的终极BOSS。典型场景是join时某个key数据量特别大导致某个reduce长时间运行其他reduce早就跑完。笔试常见的解决方向过滤掉null值或高频垃圾key如果业务上允许使用map join把小表分发到每个map任务避免shuffle对倾斜key加随机前缀拆分成多个reduce处理后再合并调整reduce数量或内存参数。但我要说一个经常被忽略的角度数据倾斜问题不只是参数调优问题它首先是一个数据理解问题。你得先知道倾斜发生在哪个key、值分布怎么样、业务上这个key是不是有特殊含义才能决定用哪种方案。面试官问这个题目真正想听的其实是你的排查思路而不是期待你直接说出加随机前缀这个答案。我自己的排查套路是这样的先看日志里哪个reduce一直卡住再把这个reduce的key抽样拿出来统计key分布然后结合业务判断这些key是正常的大key还是异常key最后再选方案。这套思路写进简答题里会显得你真正上手处理过问题。5. Spark与实时计算从RDD机制到Kafka、Flink的关键考点5.1 RDD、DAG与宽窄依赖把血缘和容错讲清楚Spark在数据开发笔试里的分量这几年越来越重。核心考点第一梯队是RDD的五大特性、DAG的构建过程、宽依赖和窄依赖的区别、Spark的容错机制。窄依赖指父RDD的每个分区最多被子RDD的一个分区使用典型操作是map、filter、union宽依赖指父RDD的每个分区可能被子RDD的多个分区使用典型操作是groupByKey、reduceByKey、join。这里考得最多的就是判断某个操作是宽依赖还是窄依赖。区分宽窄依赖的意义在于窄依赖可以在内存中管道化执行不需要shuffle宽依赖必须等所有父分区数据就绪并且会产生shuffle落盘。所以画DAG图时看有没有shuffle是判断作业stage划分的关键依据。容错机制方面RDD通过血缘实现容错。每个RDD都保存了生成自己的转换链如果某个分区数据丢失可以重新计算。宽依赖比窄依赖的重算代价大因为宽依赖丢失一个分区可能需要重算多个父分区。这个点经常和为什么Spark比MR快一起考快的一个重要原因就是RDD的血缘机制可以在内存中重算而不必像MR那样每个stage都落盘。题目还经常考RDD、DataFrame、Dataset的区别。这里给你一个速记版本RDD是元素集合DataFrame是带Schema的Row集合Dataset是强类型的对象集合。DataFrame比RDD多一层优化空间因为Catalyst可以看懂Schema做优化Dataset在DataFrame基础上加了编译期类型检查。我用过的经验是能用DataFrame就尽量别用RDD除非需要操作底层数据或者实现很复杂的UDF逻辑。5.2 Spark Shuffle与数据倾斜调优题的素材库Spark Shuffle的笔试常考点是Hash Shuffle和Sort Shuffle的演进。早期的Hash Shuffle每个map任务为每个reduce任务生成一个文件文件数 map数 × reduce数在较大规模下会直接产生太多小文件性能很差。优化后的Hash Shuffle是每个map任务只为自己所在的executor生成一份合并文件减少文件数。Sort Shuffle则利用排序加索引的方式最终每个map任务只生成一个数据文件和一个索引文件文件数量被极大压缩。数据倾斜在Spark里也常考。解法与Hive类似但要注意Spark特有的几个参数和算子比如使用repartition或coalesce调整分区数使用broadcast join代替sort merge join给倾斜key加盐后先聚合再二次聚合。这个两次聚合的思路我在项目里用过很多次第一次先带上随机前缀做局部聚合第二次把前缀去掉再做全局聚合用空间换均匀性效果很明显。5.3 Kafka分区、ISR与消费者组的必问题Kafka在数据开发笔试题里基本是固定考点。要掌握的知识点有Topic和Partition的关系、分区有序性保证、ISR机制、ack机制、消费者组和offset管理。其中一个经典题如何保证Kafka消息不丢失生产端的回答是设置acksall且重试次数调大Broker端的回答是设置replication.factor大于1且min.insync.replicas合理配置消费端的回答是手动提交offset在业务逻辑处理成功后再提交。三个端都要答全。另一个经典题Kafka为什么能支撑高吞吐要点包括顺序写磁盘、页缓存、零拷贝、批量发送和分区并行。这里要提醒一句如果你只背了答案但不懂内部机制面试官顺着问一句为什么顺序写磁盘就快答不上来就很尴尬。原因是机械磁盘顺序写远快于随机写加上Kafka使用操作系统的页缓存而不是自己维护内存缓存读取时可以复用最近写入的数据减少了用户态和内核态的拷贝次数。5.4 Flink的Checkpoint与Exactly Once语义近年校招笔试里Flink的出现频率明显上升。京东这个时间点主要考Spark但你现在准备的话Flink一定要带上。核心考点是Flink的State、Checkpoint机制、Exactly Once怎么实现、Watermark和窗口。Checkpoint机制是Flink的保命符JobManager周期性向Source算子注入BarrierBarrier随数据流一起流动每个算子收到所有输入通道的Barrier后将当前状态异步快照到持久化存储如HDFS。一旦任务失败从最近一次完成的Checkpoint恢复。Exactly Once语义则要回答两阶段提交预提交阶段把状态写入外部系统并记录事务状态提交阶段真正执行提交。这里有一个常被追问的点Kafka作为Sink时Flink的Kafka Producer如何配合实现Exactly Once答案是使用Kafka事务API在Checkpoint成功时提交事务保证数据要么被完整写入要么在恢复后回滚。Watermark这部分我建议至少能举一个乱序数据的例子并能画出窗口如何等到Watermark超过窗口结束时间才触发计算。这是面试现场最能展示我真的理解实时计算的一道题。6. SQL场景题与算法题拿下大分的关键在思路6.1 连续登录天数、分组TopN、留存率现场手写SQL的常客数据开发笔试的最后大分值题通常是手写SQL。我翻过几年的大厂笔试题最常出现的就是这三类第一类连续登录N天问题。经典的解法是用date_sub(日期, row_number() over (partition by user_id order by 日期)) 把连续行的日期对齐到同一个锚点日期然后按用户和锚点日期分组统计天数。这里要注意如果你用的窗口函数是row_number那么推导关系的核心是连续日期的值减去行号后得到同一个日期不连续的日期得到的锚点不同。第二类分组TopN问题。标准写法是row_number() over (partition by 分组字段 order by 排序字段 desc) as rn然后外层再过滤rn N。注意一个坑遇到相同分数时要用dense_rank还是row_number取决于题目要取并列还是多取一行。很多人在这个细节上失分非常可惜。第三类留存率计算。典型场景是求用户的次日/7日/30日留存。核心思路是按用户首次活跃日期作为基准然后判断该用户在后续特定日期是否有活跃记录。写出SQL的关键是先做一次首日子查询再做一次活跃日子查询然后join并做条件聚合。写SQL时我建议你遵循两个习惯先写子查询再写外层并且每个子查询都用CTEWITH子句去组织可读性会好很多写完后再把每个join的键想清楚确认粒度不会膨胀这是SQL题扣分最多的隐性坑。6.2 算法题重点关注排序、链表、BFS/DFS和堆数据开发岗的算法题难度一般介于LeetCode简单和中等之间不像算法岗那样变态但也别掉以轻心。高频考点包括链表反转、合并两个有序链表、TopK问题、二叉树的层序遍历、岛屿数量BFS/DFS、排序数组找目标值。有一个典型的笔试编码题是求TopK这个在数据开发场景里非常实用。最快的思路是用大小为K的小顶堆遍历一次数组每次和堆顶比较比堆顶大就替换并调整堆最后堆里就是最大的K个数时间复杂度O(nlogK)。如果你能进一步提到当数据量巨大时可以用分治法、采样或者MapReduce思路去做那就稳稳加分了。还有一类题是手写一个简单的WordCount用Java或Scala实现然后让你描述Spark Shell里的Scala版本怎么写。这种题本质上是考察你是否能把算法题的代码能力和大数据组件的API结合起来这也是数据开发笔试区别于纯后端笔试的标志之一。6.3 场景设计题如何衡量一个候选人的工程综合能力试卷最后偶尔还有一道开放性的场景设计题比如有一个用户行为日志表每天新增几十亿条数据需要实时统计各页面的PV/UV怎么设计这种题没有唯一答案但考察点非常集中。要先明确数据链路用Flume或FileBeat采集日志到KafkaFlink消费Kafka数据做实时计算PV直接计数UV用Bitmap或者HyperLogLog做近似去重最后写入ClickHouse或Doris供查询。讲到UV时要能说出HyperLogLog的原理和误差讲到延迟时要能说出Flink的窗口设置和Watermark策略。我记得这类题在京东这份卷子里有类似版本答案不求面面俱到但要有完整的链路意识和取舍思路。7. 备考方法上的几个建议这些建议不是从书本上抄来的是我自己在准备笔试和后来参与校招面试时的真实体会。第一个建议不要按知识点清单去背要按数据从产生到被消费的链路去串。从业务日志产生到采集到Kafka再到Hive落地ODS经过DWD清洗汇总到DWS最后产出ADS报表你要能在脑子里画出这条链路并且知道每到一个环节有哪些组件在起作用哪些参数会影响性能。用这条主线去复习无论题目怎么问你都能找到它在链路里的位置。第二个建议每个考点都尝试用面试官可能会追问什么来扩展。比如你复习了HDFS写流程就要多想一下如果写过程中某个DataNode挂了怎么办如果网络抖动导致ack超时怎么办如果客户端自己挂了怎么办这几个问题想清楚你对HDFS的理解会比你背十遍流程有用得多。第三个建议动手搭环境永远比刷题重要。校招阶段不需要多大的集群自己电脑上用Docker搭一个Hadoop Hive Spark的环境跑通几个常见作业比如Spark读取Hive表做聚合或者用Flink消费Kafka数据。笔试是纸上谈兵但如果你有真实的作业日志支撑面试聊起来会很不一样。我见过很多基础题答得不错、一聊到集群运维就露馅的同学如果你能补上这块竞争力立刻上一个台阶。第四个建议针对这份京东题建议把它当成自测清单先完整做一遍记录哪些题卡壳再回到上面四个章节里找到对应的知识点去补。千万不要拿到题目就直接看答案那样题目就白做了。做完后把错题和模糊题整理成一份属于自己的错题本考前翻一遍比再刷三套新题都稳。数据开发这个岗位在笔试阶段确实有些卷但就是因为卷才值得认真准备。把基础链路吃透把常考的知识点理解透拿到Offer并不需要运气。