从一份iHandy校招笔试题说起:大数据开发到底在考什么
每年秋招季,我都会帮身边朋友看几份大厂的笔试题,其中大数据开发方向的试卷最让我有话说。前阵子整理旧资料时翻到一份“iHandy2019校招-大数据开发工程师笔试题”,虽然年份久了一些,但里面的考点放在今天依然不过时——Hive SQL优化、Spark内存模型、Kafka消息可靠性、HDFS小文件治理,这些东西你随便打开一家互联网公司的JD,照样是笔试和面试的高频题。
先说下iHandy这家公司。它是国内较早做海外移动工具和内容产品的厂商,用户量不小,业务场景里天然带着海量日志、实时推荐、用户画像这类典型的大数据需求。所以它的校招笔试题目风格非常“务实”:不考你背了多少概念名词,而是给你一个真实业务场景,看你能不能抽出数据链路、写出可落地的方案。这篇文章我就借着这份笔试题做一次完整拆解,把题目背后的考点、解题思路、容易踩的坑全部梳理一遍。无论你是正在准备校招的应届生,还是刚转行大数据开发的工程师,这篇内容都能帮你把“笔试到底考什么”这件事看清楚。
1. 这份笔试题的底层逻辑:先搞清楚岗位要什么人
1.1 大数据开发工程师的真实工作内容
很多人对“大数据开发”的理解停留在“会用Hive写SQL、会跑Spark任务”这个层面,但校招笔试题的出题人心里很清楚:他们想要的是一个能独立解决数据问题的工程师,而不是只会调用API的“调包侠”。
以iHandy这类出海工具类产品为例,大数据开发工程师日常要面对的事情大概有这几类:第一,埋点日志的接入和清洗,客户端上报的日志格式千奇百怪,脏数据、重复数据、字段缺失是家常便饭;第二,离线数仓的建模和ETL开发,从ODS到DWD再到ADS,每一层的数据要怎么加工、怎么保证口径统一;第三,实时链路的搭建和维护,Kafka到Flink的消费延迟、数据乱序、状态后端问题,随时可能找你排查;第四,数据质量保障,调度任务失败报警、数据量突增突减、指标口径对不上,这些都需要你去定位和修复。
把这些工作内容翻译成笔试题,就是卷面上那些看似零散的知识点。Hive SQL优化的背后是数仓性能问题,Kafka消费进度的背后是实时链路稳定性问题,HDFS小文件的背后是存储成本和任务效率问题。每道题本质上都是在模拟一个真实工作场景,考察你有没有“接得住活”的能力。
1.2 校招笔试的三个考察维度
我复盘过不少大数据方向的笔试卷,发现它们的考察维度其实非常固定,基本就是三角色模型。
第一个维度是“基础扎实度”,考察你对核心组件原理的理解是否深入。比如HDFS的读写流程、NameNode和DataNode的职责、MapReduce的Shuffle过程、Spark的DAG执行机制、Hive的元数据存储。这些内容学校课程里会讲,网上博客也一大堆,但笔试不会直接问你“HDFS架构是什么”,而是会换一个业务场景,让你判断某个操作会产生什么样的底层行为。
第二个维度是“问题拆解能力”,考察你把模糊业务需求转化成技术方案的能力。比如给出一个场景:“用户点击日志按天增量入库,但数据量增长很快,查询越来越慢,你怎么优化?”这种题目没有唯一答案,出题人看的是你的思考路径——是先从存储分层考虑,还是从索引、分区、压缩入手,最后能不能给出一个可执行的完整方案。
第三个维度是“工程细节意识”,考察你对边界情况、异常场景的处理是否敏感。数据倾斜、内存溢出、小文件过多、数据重复消费、时区不一致、字符编码混乱,这些问题在真实生产环境里几乎天天遇到,笔试题里出现它们,就是看你有没有踩过坑,或者说有没有提前想过这些坑。
2. 高频考点拆解:五大组件是笔试的基本盘
2.1 Hadoop基础:HDFS读写链路不能只背结论
HDFS几乎是所有大数据笔试题的第一道开胃菜,常见考法有两种:一是让你描述一个文件的写入过程,二是让你分析某个节点宕机后的数据恢复流程。
写文件的过程其实要分清楚“客户端视角”和“内部视角”。从客户端看,调用FSDataOutputStream.write()后,数据会被切分成packet(默认64KB),放入内部队列,然后由DataStreamer线程向NameNode申请块信息,拿到一组DataNode地址后建立pipeline。这里有个容易忽略的点:第一个块写完才会继续申请第二个块,而不是一次性申请完所有块。从DataNode视角看,每个packet到达后先写入本地文件,再通过DFSClient的ack机制逐级返回确认。所以整个链路是“客户端攒包 → 建立pipeline → 逐级传输并确认”的过程。
笔试如果在HDFS上出题,还特别喜欢考“副本放置策略”。默认的三副本策略是:第一个副本放在客户端所在节点(如果客户端不在集群内,则随机选一个磁盘不忙、CPU空闲的节点);第二个副本放在与第一个副本不同机架的节点上;第三个副本放在与第二个副本相同机架的不同节点上。这么做的目的是在“容错”和“写带宽”之间做平衡——两个副本在同机架可以减少跨机架数据传输,第三个副本跨机架保证机房级别的容灾能力。这个策略不是死记硬背,而是要能讲出“为什么这么放”的道理。
2.2 Hive SQL:会用不等于能答对
Hive相关题目在校招笔试里占比很高,因为离线数仓最核心的加工语言就是Hive SQL。但笔试考的不是简单的SELECT ... FROM ... WHERE ...,而是侧重于三块:SQL语义理解、关联查询的底层执行逻辑、以及SQL改写优化。
举个例子,一道典型的笔试题是:“有两张表A和B,A表有1000万条数据,B表有1万条数据,现在要按某个key关联,问怎么让MapJoin生效?”很多人会直接答“把小表加载到内存”,但笔试想看到的是更完整的链路:MapJoin需要把小表做成HashTable文件,通过DistributedCache分发到各个MapTask,然后Map端直接将大表的每条记录去HashTable里探测,跳过Reduce阶段。这里还有几个隐含前提——小表默认阈值是25MB(hive.auto.convert.join.noconditionaltask.size),超过阈值需要调整参数;而且表的类型、文件格式也会影响是否能走MapJoin优化。你把这些都答出来,才算真正理解了这个知识点。
Hive SQL还容易考窗口函数的写法,比如ROW_NUMBER() OVER(PARTITION BY ... ORDER BY ...)取分组TopN,以及LAG/LEAD算相邻差值、SUM() OVER(...)算累计值。这类题目并不难,但考场上很多人容易在PARTITION BY和ORDER BY的顺序上写反,或者漏掉OVER()子句里的排序条件。我的建议是备考时多拿具体业务场景练手,比如“统计每个用户最近7天的下单金额”,这类题目要多做几遍,形成肌肉记忆。
2.3 Kafka:概念背得熟,一追问就露馅
Kafka在实时链路里扮演的角色太重要了,所以大数据的笔试题不可能绕过它。常见考点是:Partition与Consumer Group的关系、消息的可靠性保障、消费位移的管理。
先说Partition和Consumer Group的关系,这是最基础的,但也是最多人答不完整的。一个Partition只能被同一个Consumer Group内的一个Consumer消费,但一个Consumer可以消费多个Partition。当Consumer数量大于Partition数量时,多余的Consumer会闲置。这个知识点常以“判断题”或“选择题”出现,但真正拉开差距的是追问:如果你新增了一个Consumer,Partition的分配是怎么重平衡的?这就涉及到RangeAssignor和RoundRobinAssignor的区别了——RangeAssignor按主题逐个分配,容易产生分配不均;RoundRobinAssignor把订阅的所有Partition当作一个整体轮流分配,更均匀,但重平衡成本也不低。
消息可靠性的考法更贴近生产。题目可能是:“Producer设置了acks=all,Consumer关掉了自动提交,为什么还会丢消息?”答案是:acks=all只保证消息写入所有ISR副本,但Broker落盘时是否刷盘、Consumer拉取后是否成功处理,这是另外两件事。完整的可靠性链路需要Producer端重试机制、Broker端min.insync.replicas参数、Consumer端手动提交位移三者配合,缺一不可。你能把这条链路串起来讲清楚,才算是真的理解了Kafka。
2.4 Spark:RDD血缘和DAG是必考点
Spark相关题目通常占比最大,因为现在离线计算基本就是Spark和Hive SQL混着用。笔试考Spark,最核心的考点是RDD的执行机制,具体又分成依赖关系、惰性求值、算子分类三个角度。
依赖关系这里,常考的是“宽依赖和窄依赖的区别”以及“为什么要区分它们”。窄依赖是指父RDD的每个分区最多被子RDD的一个分区使用,可以直接在同一个Executor上流水线执行;宽依赖是指父RDD的分区可能被子RDD的多个分区使用,必须做Shuffle。区分这两者的价值在于:窄依赖的容错恢复代价低,只要重算父RDD的父分区就够了;宽依赖的某个分区丢失可能要重算整个父RDD的所有分区。这个知识点的笔试考法一般是给你一段RDD的转换链,让你标出哪些是宽依赖、哪些是窄依赖,并说明Stage是怎么划分的。
惰性求值也经常考。RDD的map、filter这些转换操作并不会立即触发计算,只有遇到count、collect这类行动操作时才会真正启动任务。这个机制的好处是能合并多个转换操作、减少不必要的Shuffle,但坏处是如果同一个RDD被多个行动操作使用,它会从头计算多次。因此笔试里会出现这样的题:“对同一个RDD先count()再collect(),请问RDD会计算几次?”答案是两次,除非你主动cache()或persist()它。这种题目就是考察你对底层执行机制有没有真正理解。
3. 真题还原与解题思路:四道题一口气拆给你看
3.1 Hive场景题:连续登录天数计算
这道题算是笔试题里的“网红题”了,问法很多,核心逻辑都一样:“给定用户登录日志表(user_id, login_date),统计每个用户连续登录的最大天数。”
我看到不少人的第一反应是用GROUP BY配合DENSE_RANK窗口函数,但很多人会忽略“跨天连续”和“日期排序”的细节。正确的解题思路是这样的:先用窗口函数给每个用户按日期排序,用日期减去排序序号得到一个“分组日期”字段;如果用户这些天是连续的,那么“日期减去序号”的结果是相同的;最后按这个分组日期做聚合,就能得到每段连续登录的天数。
这里有两个关键点值得展开说。第一,日期函数要用对,比如用date_sub(login_date, rn),如果原始日期是字符串,必须先转成标准格式,否则排序和相减都会出错。第二,同一天多次登录需要先去重,否则ROW_NUMBER()排完序之后序号会偏大,导致“日期减序号”计算出错。很多人在这一步丢分,就是因为没有考虑同一天多条记录的情况。
拿到这道题,我建议你在纸上先写出标准答案,再尝试用LAG函数写第二版解法,最后比较一下两种写法的性能差异。这样笔试时不管题目穿什么马甲,你都能快速反应过来。
3.2 JVM内存模型:大数据任务的“隐形底层”
大数据笔试题出JVM题目,本质上是在考察你是否理解任务是怎么在JVM上跑的。Hadoop的MapTask、ReduceTask,Spark的Executor,都是独立的JVM进程,JVM内存管理直接影响任务的稳定性和性能。
比较典型的题目是:“Spark Executor的堆内内存和堆外内存有什么区别?如何配置?”堆内内存受JVM垃圾回收控制,适合存放对象实例;堆外内存不受GC管控,适合存放序列化后的二进制数据,能有效减少GC压力。Spark中spark.memory.offHeap.enabled和spark.memory.offHeap.size就是控制堆外内存的参数。但实际生产里,Hadoop、Spark集群大多选择堆内内存,原因是堆外内存的管理和排查更复杂,出了问题不太好定位。
还有一类题是考察OOM的定位思路,比如“Spark任务报java.lang.OutOfMemoryError: Java heap space,怎么排查?”这类题没有标准答案,但一个合格的回答应该按照“先看是不是数据倾斜 → 再看Executor内存配置 → 再看代码里的Cache使用 → 最后考虑是否要调大并行度”这样的顺序来梳理。你要把这四个排查方向都说出来,并且讲清楚每个方向的判断依据,阅卷人才能判断你是真做过还是只会背答案。
3.3 数据倾斜:分布式计算的头号杀手
数据倾斜是面试笔试都喜欢考的实战型问题,因为它是真实世界里出现频率最高的性能杀手之一。题目常见描述是:“Spark任务卡在最后一个Stage,99%的Task都跑完了,就剩一个Task跑了很久,怎么办?”
这道题一定要拆成“定位”和“解决”两个环节来答。定位环节,先看Spark UI里每个Stage的Task耗时分布,确认是不是某个Task处理的数据量远大于其他Task;再看Shuffle读写量,判断倾斜发生在Map端还是Reduce端;最后结合业务逻辑,猜出倾斜的key是什么。解决环节,常见方案有四类:加盐打散(给倾斜key加上随机前缀再拆分成多个key)、过滤异常key(如果是空值或者明显无意义的key,直接过滤掉)、提高Shuffle并行度(spark.sql.shuffle.partitions调大)、两阶段聚合(先局部聚合再全局聚合)。
这里我特别想提醒一句:很多人一遇到数据倾斜就想着加盐打散,但加盐不是万能的。如果倾斜的key本身就极少,加盐能解决;但如果倾斜的key占总数据量的30%以上,加盐反而会放大数据量。真正稳妥的做法是先从业务上分析这个大key是否合理,比如用户ID为“undefined”的空值是不是应该提前清洗掉。你在答案里体现这种“先分析、后动手”的思路,分数会明显高一些。
3.4 HDFS小文件:存储和计算双重难题
HDFS小文件问题也是一个经典笔试题。题目会这样出:“离线路由每天产生大量小文件,导致NameNode内存压力大、MapReduce任务启动慢,你怎么处理?”
这道题从存储和计算两个角度分别展开会比较完整。存储层面,小文件太多会让NameNode的元数据膨胀,因为每个文件块都要在NameNode内存中维护一条记录。文件数量到千万级别的时候,NameNode的堆内存就会成为瓶颈。计算层面,MapReduce框架中每个文件至少会启动一个MapTask,文件太多意味着Task数量暴涨,而每个Task启动和调度都有开销,任务自然就慢了。
解决方案通常会往这几个方向写:第一个是合并小文件,可以在写入端做控制,比如通过SequenceFile、ORC、Parquet这类文件格式将数据封装成大文件;第二个是在处理端用Hive的CONCATENATE命令或Spark的coalesce对结果文件进行合并;第三个是定期扫描目录,将有大量小文件的目录触发合并任务。如果你能补充一句“小文件的本质是存储和计算模型的平衡问题”,这道题的答案就会显得很有深度。
4. 笔试题里的“连环问”:考点背后的延伸逻辑
4.1 一道普通SQL题如何演变成一场压力面试
校招笔试题的精彩之处,往往不在题目本身,而在于它如何被面试官拿来延伸追问。很多笔试的简答题、SQL题,其实都是面试环节的“引子”。
以“连续登录天数”那道题为例,笔试版只需要写出正确SQL,但到面试环节,面试官可能会连续追问:这个SQL会产生多少个Stage?每个Stage里面又有哪些Shuffle?如果数据量到了亿级,你的写法会不会产生数据倾斜?能不能用Flink SQL在实时流上实现同样的逻辑?
这些追问不是在难为你,而是在考察你的知识是否成体系——你有没有把离线Hive、实时Flink、底层Spark执行原理这些内容串成一张网。我建议备考的时候,每刷完一道笔试题,就假设自己是面试官,对着这道题往深处追问自己三个“为什么”,这种方式比单纯刷题有效得多。
4.2 从笔试到线上问题:出题人真正想看到的“排错思路”
笔试题目里那些看似“偏门”的配置参数,比如spark.sql.autoBroadcastJoinThreshold、mapreduce.input.fileinputformat.split.maxsize,真的会在生产环境用到吗?答案是会用,但更重要的是出题人想看你如何定位“应该改哪个参数”的过程。
以Hive查询变慢为例,一个完整的排错流程应该是:先看是不是有数据倾斜(通过Hive日志里的task耗时分布判断)→ 再看是不是文件格式问题(底层是TextFile还是ORC,压缩格式是什么)→ 再看是不是没有走分区裁剪(WHERE条件有没有包含分区字段)→ 最后看Join方式(能不能改成MapJoin)。这四个步骤如果只答出“加资源”或“调参数”,基本就是零分卷。所以在备考时不要死记硬背参数,要理解每个参数在什么场景下被触发、改大改小会有什么影响。
5. 实战建议:这样备考,比盲目刷题更有效
5.1 梳理一份“考点自查清单”
面对五花八门的笔试题,我的建议是先给自己拉一份知识清单,逐项确认自己对知识点的掌握程度。可以参考下面这个模板:
| 模块 | 核心考点 | 自查结果 |
|---|---|---|
| Hadoop基础 | HDFS读写流程、副本放置策略、NameNode HA | 掌握/模糊/不会 |
| Hive | 窗口函数、MapJoin、数据倾斜调优、文件格式 | 掌握/模糊/不会 |
| Spark | RDD依赖、Stage划分、内存模型、Checkpoint机制 | 掌握/模糊/不会 |
| Kafka | 消费组重平衡、消息可靠性、位移提交 | 掌握/模糊/不会 |
| Flink | 状态管理、Watermark、精确一次语义 | 掌握/模糊/不会 |
| 数据仓库 | 分层建模、维度建模、拉链表 | 掌握/模糊/不会 |
标着“模糊”和“不会”的项,优先找网上的实战案例练手,不要只看文档。因为笔试考的是“应用”,不是“背诵”。每个知识点都配套一个实际场景去理解,效果会好很多。比如学Kafka重平衡,你就去想想“双十一大促流量翻了十倍,Consumer实例扩容到10个,会发生几次Rebalance,每次有什么代价”。
5.2 刷题时的两个“反套路”技巧
第一,不要只看正确答案,要看错误答案。很多笔试题的选项里,那些看似“有道理”但实际错误的选项,往往是出题人精心设计的陷阱。你可以把错题整理下来,分析为什么错,是概念混淆还是忽略了前提条件。
第二,SQL题一定要把执行计划跑一遍。本地装一个Hive或Spark环境,用EXPLAIN命令看看自己写的SQL最终会生成哪些Stage、哪些Shuffle,这样你对底层执行的感知会深刻很多。我当年备考时,这个习惯帮我避了很多坑——很多我以为的“最优写法”,EXPLAIN一看才发现Shuffle多得吓人。
5.3 时间分配:笔试时常见的最优策略
大数据笔试题通常题量不小,题型包括选择、填空、SQL编写、简答和方案设计。我的策略是:先快速扫一眼整张试卷的题目,花5分钟评估每道题的难度;把SQL题和方案设计的完整思路先写在草稿纸上,然后再动笔;如果在某道选择题上卡壳超过2分钟,先标记一下,做完后面的题再回来看。
选择题和填空题主要靠平时的概念积累,不用花太多时间;SQL题和方案设计题才是拉分项,一定要留足时间。尤其是方案设计题,哪怕你的方案不是最优的,只要逻辑自洽、步骤完整,也比空着不写强得多。
5.4 经验之谈:从笔试题反推技术栈的演进
最后说点个人的体会吧。iHandy这份2019年的笔试题放到现在,你会发现一个有意思的现象:题目里几乎没有Flink的内容,也没有数据湖相关的问题。这是因为我整理的是2019年的试卷,那会儿Flink在国内刚刚开始流行,数据湖概念更是小众。
但从备考的角度看,这个现象恰恰值得注意:技术栈是会迭代的。你今天花大量时间准备的工具,可能过两年就被淘汰了;而底层那些不变的东西——分布式系统的CAP理论、数据一致性保障、性能调优的通用方法论,才是真正值得深挖的。我见过不少候选人,Flink的API用得贼熟,但问他“为什么状态后端选RocksDB而不选内存”却答不上来。这就是典型的“只学招式,不练内功”。
所以,如果你让我给一条最实用的备考建议,我会说:把每个组件当做一个“黑盒的打开过程”去学,不要停留在“这个API怎么用”的表面,多问几个“底层是怎么实现的”“为什么这样设计”“如果出问题了怎么排查”。笔试题只是敲门砖,你真正要构建的,是面对未知问题时的一套分析框架。这套框架,才是支撑你走得更远的底气。