
2018年秋天我还在学校准备秋招。那阵子牛客网讨论区被字节跳动的校招真题刷屏其中有一批标题写着“字节跳动2018校招大数据方向第四批”的题目我反反复复刷了不下五遍。说实话这组题在当时不算最难的但它是少数能让我在刷完之后真正把Hadoop、Spark、Java并发这些散装知识点串成体系的一套题。今天重新翻出来看这组题的价值依然很能打尤其对正在准备大数据开发、数据仓库、数据平台相关岗位校招的同学来说它帮你划出的不是“背题范围”而是一条完整的能力地图这些题到底在考什么、每类题要答到哪一层、以及怎么从笔试题延伸到面试。这不是一篇带着“标准答案”的解析稿而是我从备考者视角做的复盘先看这套题背后字节在筛选什么样的人再把高频考点按Java并发、Hadoop生态、分布式理论、数据质量四个模块逐个拆开最后聊一聊怎么用这套题反推自己的复习路线。如果你已经有一定基础可以直接跳到对应章节对照自测如果你是刚开始准备建议顺着读一遍先把考点的骨架搭起来。1. 这套题在筛选什么人2018年字节大数据岗的画像与考点地图1.1 2018年字节为什么要招大数据方向招什么样的人要理解一套校招真题不能只看题目本身得先看它背后的业务场景。2018年的字节跳动信息流推荐和短视频正是爆发期用户行为日志、内容特征、广告点击流每天产生的数据量是海量的。这些数据要被收集、清洗、存储、计算再反哺到推荐模型和业务报表里靠的是完整的大数据链路。所以那一年的大数据岗位招的不是只会调API的“工具人”而是能理解整条数据Pipeline、能在海量数据场景下定位问题的人。这个定位直接体现在了题目风格上。你会发现这套题很少考“某个组件命令是什么”这种背诵型问题更多是给你一个场景比如“大量小文件怎么处理”“某个ReduceTask特别慢怎么办”让你基于原理做判断。它考的是你在真实集群环境下能不能扛住事而不是简历上的技能列表好不好看。1.2 考点地图真题覆盖的五个模块把能找到的第四批题目汇总起来看考点分布大概是这样的模块典型考查内容常见问法Java基础与并发HashMap、ConcurrentHashMap、线程池、JVM内存为什么HashMap线程不安全线程池参数怎么设Hadoop生态HDFS写入流程、MapReduce Shuffle、YARN调度一个文件从上传到落盘经历了什么Map端输出怎么到Reduce端Spark计算RDD依赖、Stage划分、算子选择宽依赖和窄依赖的区别groupByKey和reduceByKey选哪个分布式理论CAP、ZooKeeper一致性、Kafka可靠性网络分区时怎么取舍Kafka会不会丢消息数据质量与治理数据收集、清洗、质量保障怎么保证数据不重不丢数仓为什么要分层这五个模块不是独立的而是层层递进的关系。Java并发是单机处理能力的底座Hadoop生态是分布式存储与计算的基础Spark是更高层的计算抽象分布式理论解释组件为什么这么设计数据质量则是把这些技术串进业务后必须回答的问题。字节的题目高明在哪它很少直接问“CAP是什么”而是通过一个组件的行为让你反推它遵循了什么样的取舍。1.3 第四批的定位题目风格与难度字节校招那一年分了多批题目第四批从流传出来的反馈看整体难度处于中位比第一批偏基础但比后面几批更重视原理的完整性。一个很典型的特点是题目背后都会追问“为什么”。比如考HDFS写入流程最后会追问“如果第二个副本写入失败会发生什么”考MapReduce会追问“Combiner能不能随意替换Reducer”。这种追问方式在校招笔试里少见但在面试连环问里非常常见。所以第四批题其实是一套很好的“模拟面试题”它训练的不是你记住标准答案而是你在链路断裂、组件异常时能不能继续推理。我当时刷完最大的感受是光知道流程不够还得知道流程中的每一步失败时系统会怎么处理这是后来工作中排查线上问题最需要的能力。2. Java与并发那几题大数据开发的地基为什么是它2.1 集合类问题HashMap和ConcurrentHashMap的进化史字节的题里Java集合是常客尤其HashMap和ConcurrentHashMap几乎是必考。HashMap为什么线程不安全这个问题的标准回答里很多人会提到1.7版本的头插法在并发扩容时会形成环形链表导致get死循环。但更深一层的问题是1.8改成尾插法之后HashMap线程就安全了吗答案依然不是。并发put仍然可能丢数据比如两个线程同时触发扩容都往新数组的同一个槽位写后写的覆盖先写的还有size这个字段的累加不是原子的会造成计数不准。ConcurrentHashMap的考点则集中在实现演进上。1.7是分段锁默认16个Segment每个Segment独立加锁理论上支持16个线程并发写1.8抛弃了Segment改用CAS加synchronized锁粒度细化到每个桶的头节点。面试官喜欢问“为什么1.8要改成这种实现”除了锁粒度更细之外还有一个原因是1.8的ConcurrentHashMap在并发度很高时更平滑——CAS失败就自旋自旋失败再升级为synchronized而不是像分段锁那样锁住整个Segment。我当时复习时做了个小实验用8个线程同时往HashMap和ConcurrentHashMap里put跑完检查size是否正确。HashMap的size几乎每次都不一样ConcurrentHashMap则稳定正确。这个实验很简单却能让你把“线程安全”四个字从概念变成体感。后来我把这个实验写进了项目里面试时讲起来面试官明显更有兴趣。2.2 线程池参数和面试官真正想听的回答线程池是高并发场景的常考题。核心参数就五个corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory和RejectedExecutionHandler算上handler是六个。大多数人都能背下来但字节的题目会往下追问“一个任务提交进来线程池的处理顺序是什么”顺序是先判断核心线程是否已满没满就创建核心线程执行满了就尝试往阻塞队列里放队列满了才创建非核心线程非核心线程也满了触发拒绝策略。这个顺序反直觉的地方在于先填队列再扩线程而不是先扩线程。原因是线程的创建和切换是有开销的队列相当于缓冲区能吸收短时间的任务洪峰避免频繁创建销毁线程。再往下追问就是拒绝策略怎么选。四种内置策略AbortPolicy直接抛异常、CallerRunsPolicy让提交任务的线程自己跑、DiscardPolicy悄悄丢弃、DiscardOldestPolicy丢弃最老的任务。实际工作中我默认选CallerRunsPolicy因为它能把“任务放不下”的压力传导回提交方让上游自己降低提交速度而不是默默丢数据。还有一个高频坑为什么阿里规约里明确禁止用Executors.newFixedThreadPool因为它底层用的是无界LinkedBlockingQueue队列可以无限堆积任务极端情况下导致OOM。这个问题我在一批模拟题里见过好几次本质上考的仍然是“每个参数背后对应什么资源风险”。2.3 JVM与大数据作业内存调优JVM的考点集中在内存区域、GC和内存溢出排查。大数据方向考JVM不会只考八股而是会结合Spark、Flink的执行过程来问。比如Spark Executor的堆内存分为storage、execution和other三块堆外还有off-heap如果execution内存不足就会频繁spill到磁盘表现为磁盘IO飙升、作业变慢如果storage和execution发生抢占又会引发频繁的Full GC。我见过一道比较典型的题“一个Spark作业在运行中期突然OOM你从哪些方向排查”正确思路是顺着链路看先看是不是数据倾斜导致某个Task的数据量超出预期再看是不是executor内存配置偏小再看是不是有大对象被反复拷贝最后看是不是driver端收集了过多结果。很多人一上来就调spark.executor.memory其实大部分OOM都是数据倾斜引起的加了内存反而掩盖了问题。还有面试官喜欢从“内存溢出和内存泄漏的区别”切入。内存泄漏是对象已经不需要了但GC无法回收内存溢出是内存真的不够用了泄漏往往是溢出的原因之一。排查泄漏可以用jmap导堆转储再用MAT看支配树排查溢出要先通过jstat看GC频率判断是年轻代还是老年代的问题。这些工具在大数据环境调优里同样适用毕竟Spark的Driver和Executor都是JVM进程。3. HDFS、Shuffle、RDD依赖Hadoop生态题要答到哪一层才算过关3.1 HDFS写入流程与副本放置策略HDFS写入流程是这套题里我一定会画图讲清楚的一道题。完整流程是这样的客户端调用DistributedFileSystem.create向NameNode发起请求NameNode检查文件是否存在、权限是否足够然后在命名空间里注册文件但不分配数据块接着客户端开始写数据先写本地的DataStreamer缓冲缓冲满一个chunk默认512字节后打包成packet默认64KB再往第一个DataNode发送。这里有个关键点客户端不是把数据写满一个数据块再传下一个而是边写边传形成一条pipeline。第一个DataNode收到packet后会保存一份然后转发给第二个DataNode第二个再转发给第三个。每个packet写完后DataNode会沿着pipeline反向发送ack客户端收到ack才继续发下一个packet。这样设计的好处是数据在每个DataNode上都落盘了客户端确认的是“三个副本都写成功了”而不是“我发出去了”。副本放置策略也是高频考点。默认是第一个副本放在客户端所在节点如果客户端不在集群内就随机选一个不太忙的节点第二个副本放在与第一个不同机架的节点第三个副本放在与第二个相同机架的不同节点。这样在机架故障时还能保留两个副本同时兼顾了写入带宽和容错。2018年那会儿HDFS还是这个默认策略现在有些版本加入了更多智能调度但底层思路没变。3.2 MapReduce Shuffle全过程Shuffle是MapReduce的灵魂也是整套题里最容易被问崩溃的部分。Map端Shuffle的起点是MapTask的输出输出不是直接写到磁盘而是先写到一个环形缓冲区默认大小100MB。当缓冲区使用率达到阈值默认80%时后台线程开始溢写溢写前会做两件事分区和排序。分区是按key的哈希值决定进哪个Reduce排序是默认按key排序。如果设置了Combiner会在溢写时先做一次局部合并减少写入磁盘的数据量。多个溢写文件在MapTask结束时要合并成一个大的溢出文件合并时同样会做分区和排序。Reduce端这边MapTask完成后会通知ApplicationMasterReduceTask从每个MapTask所在节点拉取属于自己分区的数据。拉下来的数据先放到内存缓冲区不够了也溢写到磁盘最后做一次归并排序把相同key的值归并到一起再交给GroupingComparator分组每调用一次Reduce函数处理一组。面试官很喜欢追问“哪些步骤可以省略”。比如如果排序不是业务必须的可以设置job.setNumReduceTasks(0)跳过Reduce阶段的排序如果不需要分区就只用一个ReduceTask。还可以用MapReduce中的Combiner来减少网络传输但前提是函数满足交换律和结合律比如求和、求最大值可以求平均值不行。这个坑我在模拟面试里踩过当时把Combiner当成万能优化面试官一句话就让我哑了。3.3 Spark RDD依赖与Stage划分给一个Java处理日志的例子Spark相关的题目RDD依赖和Stage划分是绕不开的。窄依赖是指父RDD的每个分区最多被子RDD的一个分区使用比如map、filter、union宽依赖是指父RDD的一个分区会被多个子RDD分区使用比如groupByKey、reduceByKey、join。Spark的DAG调度器遇到宽依赖就把图切成Stage宽依赖的边界就是Shuffle发生的地方。我在备考时写过一个用Java操作Spark处理日志的小例子虽然很基础但能把RDD算子串起来。当初字节的题目里也有类似的思路只是换了个日志格式import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.SparkSession; import scala.Tuple2; public class LogAnalyzer { public static void main(String[] args) { SparkSession spark SparkSession.builder() .appName(LogAnalyzer) .master(yarn) .getOrCreate(); JavaSparkContext jsc new JavaSparkContext(spark.sparkContext()); // 读取HDFS上的日志文件 JavaRDDString lines jsc.textFile(args[0]); // 过滤出ERROR级别的日志 JavaRDDString errors lines.filter(line - line.contains(ERROR)); // 假设日志格式: IP 时间 级别 消息提取IP并计数 JavaPairRDDString, Integer ipCounts errors .mapToPair(line - new Tuple2(line.split( )[0], 1)) .reduceByKey(Integer::sum); // 按出现次数降序取Top10 ipCounts.mapToPair(Tuple2::swap) .sortByKey(false) .take(10) .forEach(System.out::println); jsc.close(); } }这个例子里filter、mapToPair都是窄依赖reduceByKey是宽依赖在reduceByKey处会切出一个新的Stage。Spark会把map端做一次combiner也就是在Map端先reduce一次减少Shuffle的数据量这点和MapReduce的Combiner思路一致。你只要理解了这条链路就明白为什么reduceByKey比groupByKey高效这也是笔试里反复出现的考题。3.4 数据倾斜这个高频痛点凡是涉及大数据的笔试题数据倾斜一定会出现。它的本质是数据分布不均匀导致某个Task处理的数据量远大于其他Task表现为整个作业卡在最后几个Task上或者某个Executor频繁OOM。常见原因和处理思路可以整理成下面这张表现象常见原因解决思路部分Task运行极慢key分布不均少量key占据大量数据加盐salting、两阶段聚合Executor OOMgroupByKey/join时单个key数据过大改用reduceByKey、增加预聚合Join时数据倾斜小表关联大表热点key广播小表、将热点key拆开处理空值或默认值堆积大量null或默认值被分到同一分区过滤空值、给空值加随机后缀我在复习时把数据倾斜当作一个专题来整理因为它的解法不是背出来就完事而是要在面试里根据场景灵活选。比如“两阶段聚合”先给key加随机前缀打散做一次部分聚合再去掉前缀做第二次聚合逻辑不复杂但需要你在现场能画出数据流向。字节的模拟题里有类似场景我当时答了思路但没细化到“随机前缀的范围怎么定”面试官追问后就露怯了。建议把每种方案的适用条件和代价都提前想清楚。4. CAP、ZAB与Kafka分布式理论题背后的推导逻辑4.1 CAP别只背结论CAP理论几乎是大数据岗位的必问题但很多人只背了“一致性、可用性、分区容忍性三者不可兼得”的结论一问到实际系统就懵了。正确的理解方式应该是网络分区P在分布式系统里是必然的不是可选条件所以实际是在C和A之间做取舍。ZooKeeper是典型的CP系统Leader和Follower之间数据强一致但Leader宕机后要经过选举和恢复这段期间集群不可写这就是牺牲了A。而有些NoSQL系统更偏向AP分区时允许不同节点读到不同数据等网络恢复后再同步。Kafka则是可以做配置的默认是高吞吐的AP倾向但通过调整acks参数和min.insync.replicas可以做得接近CP。这里有一个常见的误区有人认为“P可以不要”比如单机数据库就没有分区问题。但CAP里的P关注的是“发生网络分区时系统能不能继续工作”只要系统是分布式的P就是前提条件回避不了。面试官追问到你这句话基本上就知道你是真懂还是背概念了。4.2 ZooKeeper的ZAB协议与它在Hadoop生态里的角色ZooKeeper在大数据生态里地位特殊HDFS的HA靠它做Active NameNode的选举Kafka的控制器选举也依赖它很多分布式锁也是基于它实现的。所以这套题里ZooKeeper的ZAB协议基本是躲不开的。ZAB协议的核心是两阶段提交和崩溃恢复。Leader收到写请求后先给请求分配一个全局单调递增的事务IDzxid然后广播给所有FollowerFollower把事务写入本地日志后返回ACKLeader收到过半ACK就提交事务再广播COMMIT消息。如果Leader在半途崩溃剩余节点会通过选举选出新的Leader选举规则是拥有最新zxid的节点优先然后所有节点用新Leader的日志来同步保证已经提交的事务不丢失。这个协议解释了为什么ZooKeeper适合做协调者而不适合做大数据存储它要求过半确认写性能上不去它牺牲了可用性Leader故障期间要停机选举。但协调者需要的就是强一致和可靠所以这个取舍是合理的。回答这类题目时不要只背协议名字要把“为什么要过半确认”“为什么选举要选zxid最大的”讲清楚这才是面试官想听的部分。4.3 Kafka为什么“快”又怎么保证“不丢不重”Kafka的题目在2018年的巴이트题目里出现频率很高因为它几乎是当时大数据链路中消息队列的标配。第一个高频题是“Kafka为什么快”答案主要落在三点顺序写磁盘、页缓存和零拷贝。Kafka的消息都是追加写入分区日志文件顺序写比随机写快几个数量级同时它依赖操作系统的页缓存读写都尽量走内存零拷贝则省去了数据在内核态和用户态之间的多次拷贝。第二个高频题是“Kafka怎么保证消息不丢”。要分角色回答Producer端设置acksall表示要等所有ISR副本都确认才算发送成功同时开启重试机制Broker端设置min.insync.replicas比如设置为2表示至少有两个副本同步才接受写入避免Leader单点故障时丢数据Consumer端则要注意拉取消息后先处理业务再提交offset防止处理失败但offset已经提交导致消息丢失。关于“不重”Kafka 0.11之后引入了幂等性Producer和事务enable.idempotencetrue时Producer发送的消息带序列号Broker端会去重这样能保证生产端到Broker的不重。但端到端的精确一次还需要消费者配合要么用事务要么把offset和业务结果写入同一个存储做原子提交。这个知识点在笔试里多以选择题出现但在面试里会追问到底值得花时间吃透。我当时把“消息不丢不重”整理成了一张Producer端和Consumer端的分角色表面试时照着链路讲逻辑清晰很多。5. 数据质量与数据管道最容易被低估的一类大题5.1 “减少错误、保证质量”这句话出现在笔试题里热搜词里有一句话很传神“对于大数据而言最基本、最重要的要求就是减少错误、保证质量。”这句话在字节的真题里不是口号而是会落地成题目。常见问法是“大数据收集阶段怎么保证数据质量”或者让你设计一个数据质量监控方案。数据质量的维度一般包括完整性、准确性、一致性、及时性和唯一性。“完整性”看数据有没有缺失比如用户日志里session_id为空“准确性”看数据值是否符合真实情况比如埋点里的金额是否为负数“一致性”看同一个指标在不同报表里是否对得上“及时性”看数据延迟是否在可接受范围内“唯一性”看有没有重复记录。笔试里如果出现这种题不能只回答“做清洗”要拿出具体手段在采集端加校验规则在存储端做约束在计算端做比对在应用端做监控告警。我记得有一道模拟题是“埋点日志里出现大量重复数据你怎么排查”正确链路是先看采集端是不是重复上报再看Flume是不是重复读文件再看Kafka消费端是不是重复处理最后看数仓ETL里有没有重复join产生膨胀。这种逐层排除的思路比直接说“我用redis做去重”要有说服力得多。5.2 数据收集阶段的质量问题与对策数据收集是整个链路的第一环也是最容易出问题的一环。字节的业务里埋点数据来自客户端、服务端日志、第三方数据任何一个环节都会产生脏数据。常见问题有埋点字段缺失、类型不匹配、时间戳乱序、数值越界、重复上报。针对这些问题我在项目里常用的方案是“三道防线”第一道防线是校验在数据入口处用JSON Schema或Avro Schema做约束字段类型不对直接丢弃或走死信队列不能影响主链路第二道防线是对账定期比对数据源和数仓的数据量总量波动超过阈值就告警第三道防线是监控对关键指标做实时监控比如每分钟上报量、错误率、延迟一旦指标异常就触发告警。这套思路在笔试里是可以直接写进答案的。面试官要看到的不是“我会清洗数据”而是你有没有建立质量保障体系的意识。尤其是“走死信队列”这个细节很多新人根本想不到但它是生产环境里不阻塞主流程的关键设计。我当时在项目复盘里专门写了一段“死信队列如何反哺埋点治理”面试时效果很好。5.3 数据管道里的容错与重跑数据质量还牵涉一个很重要的考点数据管道出错了怎么办。数据管道通常是分层架构ODS贴源层、DWD明细层、DWS汇总层、ADS应用层。某一层出了数据质量问题修正后要能重跑而且重跑不能造成重复数据。这里有两个关键技术点。一个是幂等写入也就是同一份数据无论写几次最终结果都一样。实现方式包括用唯一键去重、先删后插、基于分区覆盖写。另一个是Offsets管理消费Kafka时把offset记录在外部存储里和数据结果一起提交这样既能保证不丢也能在重跑时从记录的offset继续消费。我还记得一套模拟题里问过“某一天的报表数据和前一天对不上怎么定位”。答案不是直接去改SQL而是按层级排查先看ODS层的原始数据是否完整再看DWD层是否有重复再看DWS层汇总口径是否变化最后看ADS层是不是刷新任务失败了。每一层都有自己的数据质量校验规则比如“DWS层的数据不能大于DWD层的明细对应值”这种规则能帮助你快速缩小问题范围。笔试答题时能给出这种分层排查的思路说明你是真做过数仓的。6. 从刷题到面试复盘这套真题后的大数据岗备考路线6.1 刷题策略和时间分配这套题的价值在于它的覆盖面但你也别指望刷完一遍就能通关。我当时用的是“三轮刷题法”第一轮按模块刷把每个考点的原理题都过一遍不追求速度追求能对着白纸把流程画出来第二轮只刷错题和卡壳题每道题写一个“结论-原理-场景-坑”的四层复盘第三轮模拟面试找同学互相提问模拟面试官从答案中挑一个点连环追问。时间分配上Java并发和Hadoop生态各占三成Spark和分布式理论各占两成数据质量整理出一份专门的笔记就行。别把时间平均花在每个知识点上字节题目的权重很明显Java的集合和线程池、HDFS写入流程、MapReduce Shuffle、Spark的宽窄依赖、Kafka的可靠性这五个点几乎是必考的。我当时把这几块背到能默写面试里遇到相关题目心里就有底了。6.2 面试答题的STAR式讲法项目如何讲笔试过了之后是面试面试里最常问的是“讲一个你做过的项目”。很多人在这个环节翻车不是项目不行是不会讲。字节的面试官偏向追问细节而且会一直问到你说不出为止目的就是看你项目的真实深度。我推荐用STAR结构来讲项目Situation项目背景和要解决的问题Task你具体负责的任务Action你采取的技术方案和为什么这样选Result最终效果和数据指标。但光有STAR还不够要准备两个“深挖点”。比如你讲了“用Spark处理日志”面试官很可能会接着问“你用的什么算子”“数据倾斜怎么办”“Shuffle数据量多大”“怎么保证不丢数据”。这些细节必须提前准备好不然现场会卡壳。一个好用的方法是每个项目都准备一条完整的“数据链路复盘”数据从哪来、经过哪些组件、每一步做了什么、哪里可能失败、失败了怎么恢复。这套思路会把面试官的问题从“你用过什么”引导到“你怎么解决问题的”方向上局面会主动很多。6.3 现在的备考建议别照抄2018年的题但要继承它的思路2018年的题目到现在已经过去几年了组件版本和生态都在变化比如Spark SQL和Flink的地位已经大幅提升当年的题目里Flink相关内容很少现在则是必考。所以我不建议你把这套题当成“押题卷”而是当成能力清单Java并发、分布式存储、计算引擎、消息队列、数据质量这些能力模块到今天依然是大数据岗位的核心。如果你现在才开始准备建议在这套题的基础上把Spark SQL的优化比如AQE、动态分区裁剪、Flink的Exactly-Once机制、数据湖Iceberg/Hudi的基本原理补进知识体系。字节现在的题目会更贴近实时计算和数据湖但底层对原理的追问风格没变。把第四批这套题刷明白了等于拿到了一个稳定的分析框架以后遇到新组件你也能用同样的方式去拆它解决什么问题、核心机制是什么、失败时怎么处理、和同类组件比怎么取舍。最后说一句我刷完这套题后最大的体会。第一遍刷的时候很多题我看着都眼熟但真让我完整写出来却写不完整尤其是Shuffle那部分我自以为懂了后来在纸上画流程时才发现自己漏掉了环形缓冲区的溢写阈值和Combiner的触发时机。后来我把每道题都按“结论-原理-场景-坑”四层去复盘效果完全不一样。这套题你不需要背答案但一定要能从头到尾把链路讲顺。能做到这一点你拿下的就不只是字节这一场笔试而是所有大数据岗的校招关卡。