爱奇艺2019秋招大数据开发方向这套B卷,我在整理的时候其实很有感触。它出的题目不算偏,但覆盖面很广,从Java基础到分布式计算再到数仓建模都有涉及,几乎把大数据开发岗位日常要碰的东西都串了一遍。对于准备校招的同学来说,这份卷子就像一个很好的“体检单”,能帮你看清楚自己的知识体系哪里还有漏洞。我自己在带新人或者帮朋友做面试辅导的时候,也经常拿类似的题目来做摸底。这篇文章我就从这套笔试题出发,把里面涉及的核心知识点、容易踩的坑,以及我个人的一些解题思路和备考经验,一块儿聊聊。
1. 先聊聊这套题背后的业务逻辑:爱奇艺为什么要这么考
很多同学拿到笔试题的第一反应是“赶紧刷题”,但我觉得更聪明的做法是先想清楚一件事:出题人到底在考察什么。爱奇艺这种体量的视频平台,大数据开发岗位的工作内容和一般互联网公司相比,有它的特殊性。
1.1 视频平台的数据特征决定了考察方向
爱奇艺的核心业务是视频内容的制作、分发和变现,这意味着它每天要处理的数据量级非常大,而且数据类型特别丰富。用户行为日志(播放、暂停、拖动、退出)、视频内容元数据(标题、分类、标签、演员)、广告投放数据、CDN分发日志、会员订单数据等等,统统要汇聚到大数据平台上来。
举个例子,一个用户看一部电视剧,这一条播放记录在后台就会产生至少几十个维度的指标,包括播放时长、码率、清晰度、设备类型、是否拖拽、缓存命中情况等。每天几千万甚至上亿的用户在看视频,产生的数据条目用“亿”来做计量单位都是保守的。所以,爱奇艺的大数据开发同学,日常做的最多的事情就是处理这些海量日志的采集、清洗、存储、计算和分析。
这也就解释了为什么笔试题里会大量考察Kafka(日志采集和消息中间件)、HDFS(分布式存储)、Spark和Hive(离线计算)、Flink(实时计算)这些组件。因为这些东西就是日常工作台上的“锅碗瓢盆”,你如果连这些工具都不熟,那确实没法干活。
1.2 从题目反推岗位能力模型
这套B卷我整体看下来,它其实在验证三个层次的能力。
第一层是语言基础,主要是Java。为什么考Java不考Python?因为大数据生态里的核心组件,比如Hadoop、Hive、Spark(部分)、Flink,绝大多数是用Java或Scala写的,底层源码是Java,你要做二次开发、排查问题、调优,不懂Java会很吃亏。而且很多公司的开发框架也以Java为主。
第二层是计算引擎原理,这部分是区分度的关键。能不能说清楚HDFS的读写流程、Spark的Shuffle机制、Hive的SQL底层怎么转化成MapReduce或Spark任务,这些决定了你是一个“只会调API的调用者”还是一个“真正理解系统的开发工程师”。爱奇艺的题里明显偏向后者。
第三层是工程能力和场景设计。比如给你一个视频播放日志统计的需求,让你设计一套从数据采集到报表展示的完整方案。这种题没有标准答案,考察的是你有没有真正在项目里从头到尾做过东西。
把这三点想明白了,再去复习每一道题,你就能抓住重点,而不是陷入知识点碎片化的死记硬背。
2. 笔试核心考察模块:知识点分布与解析
这套B卷的知识点覆盖范围比较典型,我大致把它分成六个模块,下面逐个说。
2.1 Java基础:不只是语法,更是并发和集合的底层机制
Java基础在大数据笔试题里从来不是简单的语法考察,而是集中在集合类、并发编程和JVM内存模型上。高频考点包括HashMap的底层实现、ConcurrentHashMap的分段锁机制、ArrayList和LinkedList的区别、线程池的核心参数含义、volatile和synchronized的区别、以及JVM的类加载过程和GC算法。
我当时复习的时候,周围很多同学觉得Java基础简单,随手翻翻就过了。但实际上,像HashMap为什么在JDK 8之后要引入红黑树、ConcurrentHashMap在JDK 8之后为什么废弃了分段锁改用CAS+synchronized,这些细节如果不深入源码层面去理解,到了考场上真的会发慌。因为这些变化不是孤立的技术升级,而是和并发场景下的性能瓶颈直接相关,爱奇艺这种高并发场景下,Redis缓存、Kafka生产者消费者、Spark任务调度,到处都涉及并发编程,所以考察这部分内容是完全合理的。
2.2 Hadoop生态组件:HDFS、MapReduce、YARN
Hadoop是很多大数据平台的底座,虽然现在很多公司已经在用Spark和Flink做计算,但HDFS作为分布式存储层的位置几乎是不可撼动的。笔试里HDFS相关的核心考点就那么几个:NameNode和DataNode的角色分工、数据块副本机制和默认副本数、读写流程中的关键步骤、以及SecondaryNameNode到底做不做热备份(这里是个经典误区,它其实只做定期合并edit logs,不能故障热切换)。
MapReduce虽然在实际生产中直接使用的频率在降低,但作为理解分布式计算思想的“第一课”,笔试依然经常考。重点包括:Map阶段和Reduce阶段的shuffle过程、分区(Partitioner)的作用、Combiner能不能改变最终计算结果(要注意使用场景)、以及数据倾斜在MapReduce里怎么处理。这些底层的原理,实际上直接延续到了Spark的Shuffle机制里,你把这层纸捅破了,后面学Spark会轻松很多。
YARN的考点相对更偏资源调度,比如ResourceManager和NodeManager的关系、Container的概念、以及一个Application的提交和运行流程。这块内容比较容易被忽略,但它对应的是大数据集群的资源管理问题,实际工作中排查任务失败、资源不足这类问题,都需要对YARN有清晰认知。
2.3 分布式计算引擎:Spark为主,Flink为辅
2019年的秋招,实时计算的市场基本已经是Flink的天下,但离线计算仍然是以Spark为主力。所以这套B卷里,Spark的内容占比很高,Flink也开始出现。
Spark的考察主要集中在几个方面:一是RDD的特性和依赖关系(窄依赖/宽依赖),这是理解Spark任务划分的基础;二是DAG的生成和Stage的划分逻辑,这个几乎每年必考;三是Shuffle过程的原理和优化;四是Spark SQL的执行流程,包括Catalyst优化器的作用;五是Spark Streaming和Structured Streaming的区别。
我在辅导同学的时候发现一个常见的盲区:很多人能背出Spark的术语,但不理解RDD的“血缘关系”和“容错”之间的关系。面试官只要追问一句“如果一个节点挂了,Spark怎么恢复丢失的分区数据”,很多人就卡住了。实际上,RDD通过记录自身的依赖链条(即血缘),可以在某个分区数据丢失的时候,从父RDD重新计算,这就是Spark容错机制的核心思想。这部分虽然不会让你手写一个Spark任务那么直接,但它是区分“真懂”和“背概念”的试金石。
Flink在笔试里出现的题型,更多是概念性对比,比如Flink的窗口机制(滚动窗口、滑动窗口、会话窗口)、事件时间和处理时间的区别、Exactly-Once语义怎么实现、以及Checkpoint机制的基本流程。因为实时计算岗位通常单独招人,大数据开发方向对Flink的要求一般到“理解并能简单使用”这个层面。但如果你的简历里写了熟悉Flink,那么面试官就很可能往深了问,这时候你就得能说清楚状态后端(StateBackend)、反压(Backpressure)这些进阶概念了。
2.4 Hive与数仓建模:从SQL到底层原理
视频平台的数据分析需求极其旺盛,运营要看PV/UV、播放时长分布、会员转化漏斗、内容热度排行等等,这些报表数据绝大多数是从Hive数仓里来的。所以Hive的考察权重很高。
平时大家写Hive SQL的时候感觉很爽,像写普通SQL一样,但笔试里会问:一条Hive SQL是怎么一步一步变成MapReduce任务执行的?这里面就涉及Hive的架构组件:MetaStore(元数据存储)、Driver(解析器、编译器、优化器、执行器)以及和YARN的交互过程。如果你能画出来一条SQL从提交到返回结果的完整流程图,这道题基本就稳了。
数仓建模的考察,通常是以场景题的形式出现,比如“如果让你给爱奇艺的会员业务建一个数仓,你会怎么分层”。参考答案一般要提到ODS层(原始数据层)、DWD层(明细数据层)、DWS层(汇总数据层)、ADS层(应用数据层)。每一层的作用不一样:ODS就是原样接入业务数据,不做太多加工;DWD做清洗、脱敏、维度退化,统一成明细宽表;DWS按照主题做轻度汇总,比如按天、按小时汇总用户维度的指标;ADS直接面向业务方出报表或者接口数据。这套分层方法论在互联网公司基本是标配,笔试里答出来,面试官就觉得你确实有数仓项目的经验,而不是只在LeetCode上刷了几道题。
2.5 消息队列与缓存:Kafka和Redis是高频配角
Kafka在日志采集链路中扮演的角色太重要了。一套典型的离线数仓架构里,业务服务器的日志先发送到Kafka,Kafka再作为数据源被Flume、Spark Streaming或者Flink消费,最后落地到HDFS或者数仓里。所以笔试里Kafka几乎也是必考的。
Kafka的考察点主要包括:主题(Topic)和分区(Partition)的关系、分区内消息有序而分区间无法保证全局有序、生产者消息发送的三种确认机制(acks=0, 1, -1)、消费者组(Consumer Group)的消费模式、以及Kafka怎么通过ISR机制保证消息不丢失和不重复。2019年这个时间点,Kafka的事务消息和幂等生产者也开始出现在面试题里,不过相对偏门。
Redis在笔试里更多的是以缓存设计题的形式出现,比如缓存穿透、缓存击穿、缓存雪崩的区别和解决方案。这三个概念看起来都是“缓存出问题了”,但出问题的位置和应对策略完全不同。缓存穿透是查询了一个不存在的key,导致请求直接打到数据库;缓存击穿是某个热点key过期瞬间,大量请求同时打到数据库;缓存雪崩是大批key同时过期,或者Redis宕机,导致所有请求打到数据库。对应方案分别是布隆过滤器、互斥锁+逻辑过期、加随机过期时间+集群高可用。顺带还会考Redis的数据类型、过期策略、持久化方式(RDB和AOF)等基础问题。
2.6 算法与数据结构:不卷LeetCode,但基础题要拿分
大数据开发岗位的算法题,整体难度不会像后端开发那么高,一般集中在数组、链表、字符串、二叉树、排序、哈希表这些基础数据结构上。题目类型包括手写快排、二分查找、链表反转、括号匹配等等,偶尔会出现一道中等难度的动态规划。
为什么大数据开发也要考算法?因为在大数据处理场景中,你会频繁遇到数据去重、TopN统计、归并排序等场景。比如用堆来实现海量数据中的TopK问题,就是MapReduce里常用思想的延伸。而且,写算法题能直接看出候选人的代码能力和逻辑思维,这是做任何开发工作的基本功。
我的建议是把LeetCode热题100里的简单题和中等题刷一遍,重点掌握数组、字符串、哈希表和二叉树这几类。大数据方向不需要死磕困难题,性价比不高。但要注意,笔试题里的算法题通常要求你写核心逻辑,而不是像LeetCode那样光写个方法体,有时候还会要求你分析时间和空间复杂度,所以平时练习的时候不要只追求通过用例,要把思路和复杂度分析一并想清楚。
3. 从一道场景题看大数据的完整思维链条
笔试题的压轴部分,往往是场景设计题。我拿爱奇艺最典型的一个需求来做例子:统计全网视频按天的播放量Top10榜单。这道题我觉得特别有代表性,因为它没有标准答案,但能看出一个候选人对整个大数据处理链路是否形成闭环思维。
3.1 需求拆解:先竖立正确的目标
看到这个需求,很多人第一反应是“那直接select xx from xxx group by xxx排序不就完了吗?”如果你这么想,说明你还没有建立起大数据开发的体系化思维。这个需求的关键词是“全网”,这意味着数据体量巨大;关键词是“Top10榜单”,这意味着不仅要统计,还要排序取前10;关键词是“按天”,这意味着是离线计算场景。
正确的解题思路,是要先明确这个榜单的统计口径是什么。比如,播放量是指“播放请求次数”还是“实际播放时长超过多少秒才算一次有效播放”?爱奇艺这种平台,一定会定义严格的指标口径,否则数据出来大家都是糊涂账。在实际笔试题里,如果能把统计口径的假设说明白,并且指出业务方需要和我们确认,这种细节反而是加分项。
3.2 数据从哪来:设计采集和传输链路
播放日志从用户客户端产生,首先要经过一个埋点SDK,上报到日志服务器,然后日志服务器把数据发到Kafka。为什么中间要加一层Kafka?因为日志服务器产生的数据是突发性的,比如晚间高峰时段,数据量会暴增,如果不加缓冲,下游直接写HDFS很容易被打垮。Kafka用它的分区机制来扛住高吞吐写入,同时为下游多个消费者(比如实时计算和离线计算各来一份)提供了数据复用的可能。
在笔试题里,就要把这条链路写清楚:客户端SDK -> 日志服务器 -> Kafka(Topic: play_log,根据视频ID或者用户ID进行分区) -> Flume或者直接由Spark任务读取Kafka数据 -> 落入HDFS的原始日志目录。这里面还要考虑为什么要进行分区,如果按照视频ID进行分区,那么同一个视频的日志会进到同一个分区,这样在后续统计的时候,消费端可以利用局部性优化并行度。
3.3 数据怎么算:离线计算流程设计
落到HDFS的原始日志,下一步要进入Hive数仓。首先是ODS层,直接映射原始日志表,表结构就是日志的原始字段。然后是DWD层,做数据清洗,比如过滤掉user_agent为空的记录、过滤掉播放时长小于5秒的记录(防刷)、处理时间字段的格式统一。接下来是DWS层,按照“日期+视频ID+视频标题”的维度做聚合,计算每个视频每天的播放次数和播放总时长,这一步通常会产出一张按天分区的汇总表。
最后在ADS层,做Top10的榜单计算,可以用SQL直接在DWS表上做排序,但要考虑表的数据量。如果DWS表是亿级别的数据量,在SQL里直接order by会触发全量排序,性能堪忧。更合理的方式是先用SQL做一次Rank,或者用Spark的窗口函数row_number()按播放量倒序排列并筛选rank <= 10,这样能有效避免大表全排序。
这里还有一个很实际的问题:如果今天的原始日志量非常大,比如好几TB,用什么方式去加速处理?如果只是纯Hive SQL,默认的MapReduce引擎比较慢。这时候可以顺手提一句“用Spark on Hive来跑,并设置动态分区和合理的并行度”,甚至可以考虑用小文件合并和列式存储(Parquet + Snappy压缩)来优化读取性能,这些都能看出你对性能调优是有意识的。
3.4 结果怎么用:榜单数据的输出与可视化
榜单算出来之后,不是丢一个文件就结束了。通常是把结果表同步到MySQL,或者直接用报表工具读取ADS层结果表,生成可视化大屏。同步到MySQL的时候,要注意数据量不大,直接用Sqoop或者DataX每日增量同步就行;如果希望数据实时性更强,也可以把结果写回Redis,供线上接口查询十分钟前的榜单变化。
这个场景题,其实就是在考察“端到端”的能力。你的回答里如果能把采集、存储、计算、调度、输出这个链条说清楚,再在关键节点加上自己的性能调优和异常处理思路,我觉得这道大题基本就满分了。
4. 高频考点详解:这些内容我建议你吃透
前面说的是知识版图,这里我再挑几个笔试中出现频率最高、也最能拉开分差的考点,展开讲讲原理和实际处理思路,这样你复习的时候也能更有方向感。
4.1 数据倾斜:大数据笔试的“钉子户”
数据倾斜这个问题,几乎在所有大数据相关的笔试面试里都会出现。它的本质是数据分布不均,导致某个task处理的数据量远超其他task,整个作业的运行时间被这个“最慢的长尾任务”拖住。
在MapReduce和Spark里,数据倾斜最常见的发生位置是Shuffle阶段,比如group by的key分布不均、两张大表join时关联键大量重复、或者distinct计数时某个值特别多。笔试里一般会问你“遇到数据倾斜怎么办”,其实答案是有套路的。第一,先定位是哪一阶段出了问题,看task的运行时长和shuffle读写数据量,或者看Spark UI里某个stage的处理数据量是不是远超中位数。第二,根据原因做针对性解决:如果是join倾斜,可以把热点key单独拆分,加随机前缀再广播小表;如果是group by倾斜,可以先做局部聚合再加盐做二次聚合;如果是空值导致的倾斜,可以考虑给空值加随机后缀,或者把空值过滤掉单独处理。第三,排查数据源本身的字段质量,比如视频ID为空、IP地址为空等等,可能从源头就是脏数据。
我在实际工作里遇到最多的情况,其实就是空值和大key的问题。比如按照用户ID做统计时,有一批测试账号或者游客账号,数量特别大,直接导致Reducer卡死。这类问题要在数仓的ODS或DWD层就做掉脏数据处理,别等到下游引擎层面再临时解决。
4.2 Hive SQL的优化思路
Hive的题目,除了问架构原理,还会给出一段SQL,让你分析和优化。常规的优化思路包括:使用分区表和分桶表;用小表join大表(或者反过来,在Spark里用broadcast join);在join前提前过滤掉无关的数据,减少shuffle的数据量;避免SELECT *,只取需要的字段;以及合理设置reduce数量。
另外一个比较隐蔽但高频的考点是数据文件格式的选择。面试官可能问你,为什么推荐使用Parquet而不是纯文本格式?答案的核心在于列式存储带来的好处:查询时只读取需要的列,减少I/O;配合压缩算法可以获得很高的压缩比;Parquet还内嵌了schema信息,读数据时更安全。这个知识点在真实业务优化中非常常见,因为数据量大的时候,存储格式和压缩方案的选择直接影响成本和查询速度。
4.3 Spark作业的基本调优
Spark的调优是另一个重点。常考的方向包括:内存模型(堆内和堆外内存、执行内存和存储内存的划分)、RDD的分区数设置、Shuffle Partitions的调整、使用Kryo序列化器减少内存占用、以及Spark动态资源分配(Dynamic Allocation)的原理。
有一个点特别值得注意,就是很多同学会不理解shuffle分区的数量和并行度之间的区别。Spark参数spark.sql.shuffle.partitions是在SQL操作(join、group by、distinct)时,控制shuffle后生成多少个分区的,默认是200个。如果数据量很小,200个分区白白产生200个task,调度开销很大;如果数据量巨大,200个分区又不一定够用。所以这个参数要结合数据量和集群资源来动态调整。在实际项目中,我倾向于先跑一次小数据量验证逻辑,再根据任务表现逐步调参,而不是一上来就拍脑袋写个大的数字。
另外,广播变量(Broadcast Variable)和累加器(Accumulator)也是高频考点。广播变量是把一个只读的大变量复制到每个executor上,避免每个task都传送一份副本;累加器则是实现全局只增变量的机制,常用于在driver端汇总各个task的运行指标。二者在源码层面都很有讲头,笔试里至少要知道它们的使用场景和区别。
4.4 Kafka的可靠性保证:消息不丢失和不重复
Kafka相关的题,最后经常会落到两个核心问题上:消息会不会丢?消息会不会重复?这其实对应的是分布式系统里的“At-Least-Once”、“At-Most-Once”和“Exactly-Once”三种投递语义。
先说消息不丢,要分三端来讨论。生产者端,要设置acks=all(或者acks=-1),并且确保消息发送是同步等待回调或者使用事务API;Broker端,要设置副本因子大于1,并且min.insync.replicas要合理设置,防止单点故障;消费者端,处理完业务逻辑再提交offset,避免“消费了但没处理完就提交offset”导致的消息丢失。另外,如果使用Flink作为消费者,Flink的Checkpoint机制能保证状态和Kafka offset的一致性,这是实现端到端Exactly-Once的基础。
再说消息重复,这主要出现在消费者端,因为消费者拉取一批消息后,在处理过程中崩溃了,offset没有及时提交,重启后就会重新消费这一批消息。解决方案要么是业务逻辑天然支持幂等(比如写入数据库时用唯一键约束),要么就是借助Kafka的幂等生产者加事务API,在流处理框架里实现精确一次消费。笔试里能把这个链路分析清楚,说明你真的用过Kafka在生产环境里踩过坑,而不只是看过文档。
5. 备考策略和实操心得
聊完具体的知识点,最后再来说说准备这套笔试(以及类似的大数据开发笔试)的整体策略。这些东西是我自己当初求职踩过坑,后来带实习生也反复提醒过很多次的经验。
5.1 三轮复习法:从扫盲到真题到模拟
第一轮是知识扫盲。把常用组件的基本概念、架构原理、使用场景过一遍,确保没有知识盲区。这一轮最重要的是把“是什么”和“为什么”连起来,比如不只是背“HDFS适合存大文件”,还要想清楚它为什么不适合存大量小文件,因为它每个文件的元数据都保存在NameNode内存里,小文件太多会撑爆NameNode。
第二轮是真题训练。找到往年的大数据笔试题,按模块刷,总结高频题型和答题套路。这里尤其要注重“简答题”和“场景题”的训练,不能只看选择题和判断题。动笔写一遍和心里想一遍是完全不同的,写出来的东西往往更能暴露你的逻辑漏洞。我当时准备的时候,把Kafka的消费者组流程、HDFS的读写流程、Spark的Stage划分流程分别画成流程图,反复默写,直到闭上眼睛能完整复述出来。
第三轮是模拟实战。自己给自己出一个综合性的场景题,然后按照“端到端”的思路,把采集、存储、计算、调度、分发这条链路完整写下来。甚至可以假设集群出现了某种故障,比如NameNode宕机、某个节点磁盘写满、Kafka消费者积压严重,你来写排查思路和解决方案。这种模拟训练能帮你在考场上面对灵活的大题时,不慌不忙。
5.2 题目要做完,但更要留下好印象
笔试的题量通常不小,时间比较紧张。我的建议是先把简单的、会做的题目快速做掉,拿到基础分;遇到不会的题目,宁可根据自己的理解写一点思路,也不要留白。因为很多批改是人工看的,你哪怕写得不对,只要有思路,也能看出你是有工程sense的。
还有一个容易被忽略的细节:字迹和排版。笔试如果是在线OK,代码部分一定要缩进清晰、变量命名规范。这些细节看起来不影响对错,但实际上很多阅卷官会通过这些细节来评估候选人的代码习惯。一个连变量名都乱写的人,很难让人相信他在实际工作中能写出可维护的代码。
5.3 简历项目和笔试内容的呼应
如果笔试通过进入面试,面试官一定会围绕你简历里的项目经历展开。所以准备的题目和知识点,最好能和你简历上的项目做一个映射。比如公司笔试考了Flink的窗口机制,你简历里恰好写了一个实时统计UV的项目,那么面试的时候就要重点准备这个项目当时的窗口划分是怎么做的、状态都存了什么、出现故障后怎么恢复。项目不一定要多高大上,但一定要是自己真正动手做过、能讲清楚细节的。
5.4 心态调整:笔试不是终点
最后想说一下心态。很多同学把笔试看得很重,一遇到不会的题就紧张,后面会的题也发挥不好了。实际上,大数据开发方向的笔试,本来就不可能全做完,也不会要求你全对。它的目的是在尽量短的时间里,评估你的知识面和学习潜力。遇到不会的知识点,坦然面对,把会做的部分做到最好,这本身就是一种能力。
我在实际辅导中常跟同学讲:平时多积累,考试时稳得住,这才是最关键的。你今天在这套题里暴露出来的漏洞,恰恰是你接下来几天最需要补的课。每一次笔试都是宝贵的“体检报告”,善用它,你就离offer越来越近了。
笔试题说到底是一面镜子,照出来的是你平时学习的足迹。把基础挖深,把链路打通,把细节抠透,你自然能在笔试中展现出真正的实力。希望这篇拆解能帮你理清思路,在大数据开发这条路上走得更稳。