这段时间陆陆续续有学弟学妹问我,说自己刷题、背八股、写项目,方向感还是很模糊,尤其碰到那种“看起来每个字都认识,连起来不知道考什么”的笔试题,直接心态塌方。我翻了翻手里存的老题库,发现蘑菇街2019年这套后端/基础后端/大数据实习生笔试试题,放到今天来看依然很有代表性——它不是那种刁钻到失去意义的卷子,而是能把候选人的基本功、工程思维和学习潜力都筛出来的典型电商系笔试。今天我就拿这套题当引子,把三个方向各自的考察逻辑、核心知识点、编程题解题思路,以及备考时应有的优先级,一条条拆给大家。
如果你是正在准备Java后端、大数据岗位实习或校招的同学,这篇文章应该能帮你少走不少弯路。哪怕你不是面电商,这套复盘思路也通用:把“考什么”还原成“岗位需要什么”,再从知识点回到工程实践,你会发现笔试其实就是一份浓缩版的岗位说明书。
1. 这个笔试到底在考什么:一张试卷背后的岗位逻辑
1.1 从题型结构看筛选目标
蘑菇街2019年这套实习生笔试题,整体结构基本遵循了当时互联网公司校招笔试的通用套路:客观题(选择、填空)考察基础知识的宽度,简答题考察理解深度,编程题考察动手能力。后端和基础后端方向在客观题上会更偏Java、计算机网络、操作系统、数据库,而大数据方向则会在Hadoop生态、数据仓库、离线计算这些模块上加码。
出题人真正的意图,不是让你考满分,而是想通过一套题确认三件事:第一,你的计算机基础是否成体系,而不是零散背了几个框架;第二,你有没有基本的工程敏感度,比如知不知道HashMap在多线程下会出问题、数据库索引底层到底是什么结构;第三,你的代码能力能不能达到“拿来就能干活”的底线。实习生嘛,不会指望你一来就独当一面,但一个连基础都含糊的人,团队带起来确实太累。
1.2 三个岗位方向考察重心对比
我把这几个方向的考察重心整理成了一张表,方便大家对号入座:
| 岗位方向 | 核心考察点 | 典型考察模块 | 一句话总结 |
|---|---|---|---|
| 基础后端 | 计算机基础 + Java语言功底 | 数据结构、操作系统、网络、Java集合与并发 | 考察你“有没有扎实的地基” |
| 后端 | 基础之上再加工程能力 | Java、Spring、MySQL、Redis、分布式基础 | 考察你“能不能直接参与业务开发” |
| 大数据 | 基础之上偏数据生态 | Linux、Hadoop、Spark、Kafka、数仓分层 | 考察你“懂不懂数据从哪来、到哪去” |
这里有个容易忽略的细节:基础后端和后端并不是简单的前置关系。基础后端更强调底层原理,而后端更强调应用层能力,比如框架原理、缓存设计、接口性能优化。对大数据的同学来说,Java基础同样重要,因为大部分大数据框架都是Java/Scala生态,不懂JVM、不懂并发,玩Spark和Flink是会吃大亏的。
2. 后端/基础后端核心考点:基础不牢,地动山摇
2.1 Java基础与并发:从集合到锁的连环问
蘑菇街这套题里,Java相关题目占有相当比重,而且问法非常“实战导向”。比如说,HashMap和ConcurrentHashMap的区别,几乎是必考题。很多同学能背出来“HashMap线程不安全,ConcurrentHashMap线程安全”,但一旦被追问“底层是怎么保证线程安全的”“JDK 1.7和1.8的ConcurrentHashMap有什么区别”,就卡住了。
这就是典型的“知其然不知其所以然”。在回答这类问题时,建议按三层递进来讲:第一层说结论,第二层说机制,第三层说场景。比如HashMap的问题,你可以这样说:HashMap在JDK 1.7中采用头插法,多线程put时可能导致环形链表,进而引发CPU 100%问题;JDK 1.8改成尾插法后解决了这个问题,但size、modCount等共享变量依然没有原子性保护,所以并发写场景依然不安全。而ConcurrentHashMap在JDK 1.7用的是分段锁,JDK 1.8改成了CAS + synchronized锁桶,锁粒度更细,并发度更高。
除了集合,线程池也是高频考点。蘑菇街这类电商公司,业务峰值明显,线程池参数怎么设置、任务提交后按什么顺序执行、拒绝策略有哪些,都是很实际的问题。我建议把ThreadPoolExecutor的核心参数、四种拒绝策略,以及一个简单的参数估算方法形成肌肉记忆。比如一个IO密集型任务,核心线程数可以设为CPU核心数乘2再加1,但更准确地是根据“任务耗时占比”来算,公式略复杂,笔试时能把思路讲清楚就够了。
2.2 操作系统与计算机网络:看似八股,实则筛选逻辑
操作系统和计算机网络在基础后端笔试题里属于“不能丢分”的部分。进程和线程的区别、死锁的四个必要条件、虚拟内存的作用、TCP三次握手和四次挥手、HTTP与HTTPS的区别,这些都属于计算机专业的基本盘。蘑菇街的题目不会在这些东西上出偏题怪题,但会换着花样考你“是否真的理解”。
举个典型的例子,问“为什么TCP连接建立需要三次握手,而不是两次”。很多人只会背“防止已失效的连接请求突然传到服务端,造成资源浪费”。这个回答方向没错,但要拿全分,建议补充:两次握手时,服务端无法确认客户端是否收到了自己的同步确认报文,如果客户端认为自己没连上、服务端却已经分配资源,就会造成服务端资源白白挂着。这背后的本质是“双方都需要确认彼此的收发能力”,理解了这一点,不管题目怎么变你都能接住。
网络部分还有一个容易在笔试里翻车的点,就是HTTP状态码。尤其是301和302的区别、401和403的区别,搞混的人特别多。简单记忆法:301是永久重定向,302是临时重定向;401是“未认证”,你需要先登录,403是“已认证但没权限”,服务器认得你但就是不让你进。蘑菇街这类电商对外接口很多,考察状态码其实是在看你对接口语义的理解是否准确。
2.3 数据库与缓存:电商系统的命根子
后端笔试题里,MySQL和Redis的考察权重非常高,这在电商场景下特别合理。蘑菇街的业务是导购电商,首页推荐、商品详情、购物车、订单交易,哪一样都离不开数据库和缓存。
MySQL部分我印象最深的是索引相关的题目,比如“InnoDB为什么用B+树而不是B树或红黑树”。回答时抓住三个要点:第一,B+树只在叶子节点存数据,非叶子节点能存更多索引项,树更矮,磁盘IO次数更少;第二,叶子节点用双向链表串联,范围查询和排序效率高;第三,所有查询都要走到底部叶子节点,访问路径稳定。另外,联合索引的最左前缀原则,以及explain里的type字段,从const、ref、range到index、ALL,最好都能说清楚。
Redis部分,缓存穿透、缓存击穿、缓存雪崩是老三样,但问法每年都在变。蘑菇街那套题里的问法是把三种场景混在一起,让你给出区分和解决方案。我的建议是:不要只背“布隆过滤器”“互斥锁”“随机过期时间”这些关键词,要能结合电商业务讲出具体做法,比如首页热卖商品的缓存过期时间加上随机抖动,DB层再对空结果做短暂缓存,这样冷启动时就不会瞬间压垮数据库。
3. 大数据方向考点拆解:从HDFS到实时链路的完整考察
3.1 大数据生态基础:HDFS、MapReduce与YARN的协作关系
大数据方向的笔试题,整体给人的感觉是“偏架构、偏流程”。选择题会考HDFS读写流程、MapReduce的Shuffle过程、YARN的资源调度机制,简答题则喜欢让你画一条离线数仓链路,或者对比Spark和MapReduce的优劣。
回答HDFS写流程时,我习惯用“三步走”来记忆:第一步,客户端向NameNode发起写请求,NameNode检查权限和路径合法性,返回可用的DataNode列表;第二步,客户端按数据块(默认128MB)切分数据,依次写入第一个DataNode,再由第一个DataNode并行复制到第二、第三个DataNode;第三步,所有副本写完后,DataNode向客户端返回确认,客户端再向NameNode汇报。这里有个细节容易被忽略——副本放置策略,第一个副本放在客户端所在节点,第二个副本放在同机架的另一个节点,第三个副本放在不同机架,这个策略是为了在“容错性”和“写入带宽”之间做平衡。
MapReduce的Shuffle是另一个高频考点,同时也是很多人的痛点。简单来说,Map端做完局部排序后,会把结果按分区写入内存缓冲区,缓冲区达到阈值后溢写(spill)到磁盘,同时进行合并排序;Reduce端从各个Map端拉取属于自己分区的数据,做归并排序后喂给reduce函数。理解Shuffle的关键在于“数据怎么从Map端到Reduce端”,而不是背那些术语本身。我当年复习的时候,自己画了三遍Shuffle流程图,直到能闭着眼还原每一步,笔试遇到相关题目就再也不慌了。
Spark在这个时代的笔试题里也已经占了半壁江山。常考的点包括:RDD的宽依赖和窄依赖怎么区分、Stage是怎么划分的、Spark on YARN的两种部署模式(client和cluster)有什么区别。宽窄依赖的判断标准很简单:父RDD的一个分区是否被子RDD的多个分区使用,一父多子就是宽依赖,需要Shuffle,而Stage就是按照Shuffle边界来切的。这个点一旦想通,Stage划分题目基本就是送分题。
3.2 数据链路设计题:离线数仓与实时计算
大数据方向的简答题,很多时候不会只考单个组件,而是给你一个业务场景,让你设计一条完整的数据处理链路。比如“电商平台每天产生大量访问日志,需要统计各品类商品的曝光量、点击量和成交转化率,你会怎么设计?”
这种题目没有唯一答案,但考察的是你有没有完整的数仓分层思维。比较稳的回答框架是:源数据层(ODS)先不做任何加工,将日志原样落地;明细层(DWD)做清洗、去重、维度补充,比如把IP解析成城市,把用户ID和登录表关联补齐;汇总层(DWS)按业务维度做轻聚合,比如按小时、按品类统计曝光和点击;应用层(ADS)面向具体报表需求,产出最终指标。计算引擎的选择上,离线部分用Hive或Spark批量跑,实时部分如果有分钟级延迟要求,可以引入Flink读取Kafka里的日志流做实时聚合,结果写入OLAP引擎或Redis。
我在跟很多同学交流时发现,大家不是不知道数仓分层,而是不会把组件合理地串起来。建议平时多动手搭建一个最小闭环:用Flume采集一个日志文件到HDFS,再用Spark读HDFS算一个词频统计,最后把结果写到MySQL,这样你对整条链路就有了体感,笔试答题时再也不会挤牙膏。
3.3 大数据面试中的“隐藏考点”:数据倾斜与任务调优
数据倾斜是笔试和面试都特别爱出的题,因为它直接考察你有没有处理过真实数据。题目通常是:“Spark任务跑得很慢,发现某个Stage卡了很久,你怀疑是数据倾斜,怎么定位和解决?”
定位阶段,可以从Spark UI上看各Task处理的数据量,如果某个Task的数据量明显比其他Task大几个数量级,基本就实锤了。解决手段要分情况说:如果是大表和小表Join,用广播变量把小表广播到每个Executor,避免Shuffle;如果是Key本身倾斜,可以加随机前缀,把一个大Key拆成多个小Key,再对结果做去重合并;如果是聚合类操作的数据倾斜,可以做两阶段聚合,先加随机前缀做局部聚合,再去掉前缀做全局聚合。回答时最好能带上“实际工作中我遇到过xxx情况,最后用xxx方案解决”这种细节,会让回答一下子立体很多。
另外还有一类容易被忽视的题目:Linux基础。大数据环境跑在Linux上,基本命令必须熟。蘑菇街那套题里出现过查看磁盘占用、查找某个进程、查看端口监听状态的题目,虽然分值不高,但不会写是真的会卡住。df -h、free -m、top、ps -ef | grep java、netstat -tlnp、tail -f,这些命令最好练到不用思考就能打出来。
4. 编程题实战:高频题型与手写代码思路
4.1 从蘑菇街真题看算法题的考察范围
蘑菇街后端和大数据实习生的编程题,难度大致在LeetCode Medium偏下的水平。题目类型很集中:字符串处理、数组与排序、链表操作、Top K问题、LRU缓存设计,以及互联网公司特别爱考的“手工实现某个工具类”。
我印象中有一道题很典型:设计一个LRU缓存,支持get和put操作,要求时间复杂度O(1)。这道题考察的是数据结构组合能力,标准解法是HashMap + 双向链表,HashMap用来O(1)查找,双向链表用来O(1)插入和删除。网上有很多简版写法,但很多人一紧张就把链表的头尾指针搞乱。我建议在笔试前手写至少三遍,第一遍照抄理解,第二遍合上答案自己写,第三遍边写边讲出每一步在干什么,直到形成肌肉记忆。
再看一道大数据方向可能出现的题:有一个包含10亿条用户访问记录的日志文件,每行包含用户ID和访问时间,统计访问次数最多的前100个用户。这题的最佳思路是分治:先用哈希函数把大文件拆成若干个小文件,保证相同用户ID一定落在同一个文件里,再对每个小文件用HashMap统计次数,最后用小顶堆维护全局Top 100。这类题目重点不在代码技巧,而在于你懂不懂“内存放不下就分片”的思想,这也是大数据的核心思想之一。
4.2 手写代码的边界问题与答题节奏
编程题除了考察思路,更考察代码的健壮性。很多人LeetCode刷题时直接跑通过就完事,笔试时却忽略了边界条件,比如入参是null、字符串为空、数组长度为0、数值溢出。以字符串转整数为例,如果面试官看到你写了空指针判断、符号位处理、溢出检查,心里对你的评价会明显高一档。
下面我写一个简单但完整的字符串转整数示例,大家感受一下工程化写法的节奏:
public class StringToInt { public static int myAtoi(String str) { if (str == null || str.length() == 0) { return 0; } int index = 0; int sign = 1; long result = 0; // 1. 去掉前导空格 while (index < str.length() && str.charAt(index) == ' ') { index++; } if (index == str.length()) { return 0; } // 2. 处理正负号 char first = str.charAt(index); if (first == '+' || first == '-') { sign = (first == '-') ? -1 : 1; index++; } // 3. 核心转换,注意溢出 while (index < str.length() && Character.isDigit(str.charAt(index))) { result = result * 10 + (str.charAt(index) - '0'); if (result * sign > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } if (result * sign < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } index++; } return (int) result * sign; } public static void main(String[] args) { System.out.println(myAtoi(" -42")); System.out.println(myAtoi("4193 with words")); System.out.println(myAtoi("")); } }代码不复杂,但把空值、空格、符号、溢出全部cover住了。笔试过程中,建议先花1分钟确认题目有没有隐藏限制条件,再动手写。如果某题一时没思路,不要死磕,先跳过做下一题,最后再回头补。一定要避免“一题卡半小时,后面全空着”的悲剧。
4.3 多线程编程题:后端笔试的硬骨头
后端方向的编程题有时还会出多线程相关的题目,比如“三个线程交替打印ABC循环10次”。这种题考察的是线程协作的基本功,一般可以用synchronized + wait/notify,也可以用一个共享锁和状态标志来实现。
我推荐一个通用的模板思路:把打印逻辑抽象成一个轮次判断,每个线程打印前先检查“当前轮到自己了吗”,不是就等待,是就打印并变更状态,再唤醒其他线程。这种写法虽然不如Lock和Condition优雅,但胜在思路清晰、不容易写错,笔试时稳定是第一位的。
多线程题的高分关键在于“讲清楚为什么”:为什么用while而不是if来做条件判断?因为防止虚假唤醒,这是JVM规范里明确提到的问题。为什么wait和notify要放在同步块里?因为wait释放monitor锁的前提是持有它,否则会抛IllegalMonitorStateException。这些小细节,恰恰是阅卷时区分“背过答案”和“真正理解”的地方。
5. 备考路线与答题技巧:把一张试卷转化成offer的路径
5.1 按优先级安排复习计划
很多同学在准备笔试时最大的问题不是不努力,而是东一榔头西一棒子,今天看Redis,明天刷LeetCode,后天又去学大数据平台搭建。结果一个月下来,好像什么都碰了,又什么都没有形成体系。针对蘑菇街这套笔试的考察结构,我建议按以下优先级来安排复习:
第一优先级是编程题和Java基础。每天至少保持1到2道手写代码题,LeetCode按Tag分类刷,重点刷数组、哈希表、链表、字符串、二叉树、堆、排序这七类;Java基础重点看集合源码、并发工具类、JVM内存区域和垃圾回收。第二优先级是数据库、缓存和网络,这是后端岗位的保命分;第三优先级才是各类框架原理和分布式组件,比如Spring、Spring Boot、RocketMQ等,理解核心思想即可,不必死磕源码细节。大数据方向的同学,把网络、操作系统、Java基础这三大块保住之后,再重点攻克HDFS读写、MapReduce Shuffle、Spark宽窄依赖和SQL相关题目,性价比最高。
这个复习顺序的逻辑是:编程题决定你的下限,Java基础和数据库决定你的上限,框架相关题目只要不丢大分就好。毕竟笔试之后还有面试,面试官一定会基于你的笔试答案追问,所以“每个知识点都能用自己的话讲清楚”比“背了一大堆却互相矛盾”有价值得多。
5.2 一个高效的知识点复盘方法
我本人特别推荐用“费曼式刷题法”来准备笔试:每做完一道题、复习完一个知识点,假装自己是老师,把解题思路和原理用最简单的语言讲给一个“完全不懂的人”听。如果讲的过程中发现卡壳了、逻辑不连贯了,那这个知识点就是你的薄弱环节。
比如你复习完线程池,可以试着讲:“线程池就是把新建线程变成从池子里捞,核心线程跑完也不回收,任务多了排队,队列满了就加临时线程,还不够就按策略拒绝。”如果你能在一分钟之内把这个链条清晰讲完,笔试时不管怎么出题你都不慌。如果讲不清楚,回去再看一遍源码,而不是急着看下一个知识点。
另外,强烈建议把错题和易混点收集起来,做成自己的速查手册。比如301和302的区别,Synchronized和ReentrantLock的区别,countDownLatch和CyclicBarrier的区别,B树和B+树的区别。这些成对出现的概念,在选择题和简答题里特别容易设陷阱,整理在一起对比记忆,效率高很多。
5.3 笔试题不会的题,怎么“抢救”分数
笔试时遇到完全不会的题,千万不要留空。大部分互联网公司的笔试题阅卷,不是只盯标准答案,还会看你有没有一定的分析思路和踩分点。选择题蒙一个也有概率,简答题就算思路不对,把你理解的那部分写出来,也能让阅卷人知道你的水平在哪里。
特别是大数据方向的简答题和方案设计题,只要你能够画出“数据源 -> 接入 -> 存储 -> 计算 -> 应用”这种分层架构,再写出每个环节用到的组件,就算细节不完美,也能拿到不少分。这种答题方式本质上是展示你的技术视野,而不是精准复述教科书。当然,平时还是得多积累,如果你的知识库本身就干瘪,那再怎么“抢救”也挤不出多少内容。
6. 写在最后:我的一些实际体会
回过头看蘑菇街2019年这套笔试题目,虽然年份已经过去几年,但它的考察思路和今天绝大多数互联网公司的实习生招聘相比,几乎没有本质变化。基础后端和后端岗位,看的还是那三件事:计算机基本功扎不扎实、代码能不能写利索、对工程世界有没有基本的认知。大数据方向,看的是你懂不懂数据从产生到消费的完整链路,以及遇到性能瓶颈时有没有解决问题的直觉。
我在带新人、帮学弟学妹做模拟面试的过程中,发现一个特别普遍的现象:很多人不是不聪明,也不是不努力,而是复习时太“飘”太“散”,永远停留在API使用层,没有往底层原理和设计思想上深挖一步。其实笔试题目早就圈好了范围,你越早看懂它背后在考什么,备考就越有方向感。我个人建议,拿到任何一套笔试题,不要急着刷完对答案,先花30分钟做一件更有价值的事——把每道题还原成它对应的知识点,再把这些知识点标注上优先级。当你把所有题目都拆成一张知识图谱时,你会发现需要准备的东西没有想象中那么多。
希望这次复盘能对你的笔试准备有一点启发。如果你正在准备后端或大数据方向的实习生笔试,不妨试着用我上面说的方法,把目标公司的历年真题做一次“知识点反推”,再按优先级安排复习。加油。