ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

货拉拉大数据笔试真题解析:从Hadoop到SQL的实战能力考察

货拉拉大数据笔试真题解析:从Hadoop到SQL的实战能力考察 我说实话当年看到货拉拉大数据中心的笔试题时第一反应是“这和我想的不太一样”。它不像某些大厂那样动不动就甩一堆冷门源码题压垮你反而更偏向考察一个大数据工程师“吃饭的家伙”——基础扎不扎实、有没有真实项目经验、遇到问题会不会排查。整套题做完你会清楚地感受到这家公司要的不是刷题机器而是能直接上手干活的人。虽然这是2018年的题但几年过去大数据岗位笔试的底层逻辑基本没变核心知识点依然高度重合。这份题作为自我检视的清单比很多新题都有参考价值。下面我把整套题拆开从考察逻辑、核心题目解析到备考建议完整过一遍。1. 题型分布与考察逻辑这套题到底在考什么先看整体结构。货拉拉这套笔试题大致分四个模块计算机基础与Java、Hadoop生态核心、数据采集与数据仓库、手写SQL与算法。每个模块的题量和难度其实直接反映了岗位日常工作的重心。计算机基础与Java约20分选择题为主覆盖JVM、并发、集合类。Hadoop生态核心约30分选择简答HDFS与MapReduce占大头附带YARN与Spark基础。数据采集与数据仓库约30分场景题设计题Flume/Kafka、维表设计、分层架构是重点。手写SQL与算法约20分两道SQL一道编程题考察实战输出能力。这个配比很有意思。货拉拉的核心业务是物流调度数据团队要处理的是订单轨迹、司机行为、供需预测、营销分析这类实时和离线并存的数据。所以笔试里数据采集、数据仓库设计、SQL能力占了近一半分数这完全符合业务驱动型数据团队的特点——他们不关心你能不能手撕红黑树更关心你能不能把埋点日志准确无误地送到数仓再快速算出一张业务报表。另一个耐人寻味的地方是整套题几乎没有考察深度学习或算法建模的内容。这也印证了一个行业现象大数据开发岗和算法岗的边界还是比较清晰的。大数据开发的核心矛盾是“数据量太大、链路太长、故障太多”不是“模型精度不够”。理解了这一点你备考时的精力分配就不会跑偏。2. 核心题目深度解析每一道题背后的原理与考点2.1 Java基础与并发看似送分实则筛人这套笔试题的Java部分相当一部分人会觉得简单但恰恰是这些“简单题”区分了“用过Java”和“理解Java”两类人。比如有一道关于HashMap的题问JDK1.8中HashMap在什么条件下从链表转为红黑树。标准答案是链表长度达到8且数组长度大于等于64。但真正值钱的不是这个阈值而是面试官想通过追问“为什么是8”看你有没有读过源码注释。源码里写得很清楚遵循泊松分布在负载因子0.75的情况下链表长度达到8的概率已经低到千万分之一。也就是说转红黑树本质上是极端情况下的兜底而不是常态优化。再看一道关于volatile的题问它能否保证原子性。很多人脱口而出“不能”但题目如果换个角度——volatile能否保证可见性和有序性能否保证long/double这类64位变量赋值的原子性——就有很多人掉坑里了。实际上在64位JVM中对volatile的long/double的读写是原子的但对volatile的i这类复合操作依然不保证原子性。这就是JMMJava内存模型的精髓可见性、有序性、原子性是三个不同维度不能混为一谈。再比如有一道关于线程池的题问ThreadPoolExecutor的拒绝策略有哪几种。这题本身不难AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy背过就能答。但我在实际工作中发现真正会用的人很少。多数人写线程池就是用Executors.newFixedThreadPool根本不设置拒绝策略和队列大小。在大数据场景下这会导致一个非常隐蔽的问题任务队列无限堆积内存被撑爆进程直接OOM。所以我的建议是生产环境一律用ThreadPoolExecutor显式声明参数核心线程数和最大线程数要根据任务类型估算队列用有界队列拒绝策略要结合业务决定是丢弃还是由调用线程执行。2.2 JVM知识点从内存结构到GC策略JVM部分有一道经典题Java堆内存分哪几个区域各区域的作用是什么。这道题属于背了就能答的新生代Eden、Survivor From、Survivor To、老年代。但题目如果深挖一步——对象什么时候进入老年代就有很多人说不全了。对象进入老年代有四种情况大对象直接进入老年代通过PretenureSizeThreshold参数控制、长期存活的对象在经历一定次数的Minor GC后进入默认15次通过MaxTenuringThreshold控制、动态年龄判断Survivor中同龄对象大小总和超过Survivor空间一半、Minor GC后Survivor放不下的对象。这四种情况实际工作中最常触发的是第一种和第二种而动态年龄判断往往会被忽略。还有一道关于GC的题问CMS和G1的区别。2018年这个时间点G1已经被广泛使用了。核心区别在于CMS是基于标记-清除算法会产生内存碎片且并发标记阶段会占用CPU资源导致吞吐量下降G1是基于Region的内存布局通过维护优先列表来优先回收价值最大的Region且天然支持可预测的停顿时间。如果放到今天的语境下答案还要补充CMS在JDK14已经被移除G1成为默认收集器而ZGC和Shenandoah已经在低延迟场景下崭露头角。我在面试候选人时经常用一道实战题来替代理论题线上应用频繁Full GC你怎么排查。很多人会背命令——jstat、jmap、jstack——但真到了现场思路就乱了。我一般建议按这个顺序排查先用jstat -gcutil看一下各个代的使用率和GC频率确认是不是老年代满了再用jmap -dump导出堆快照用MAT或JProfiler分析大对象和对象引用链如果是线程问题导致的用jstack看是否有线程阻塞或死锁最后结合业务代码看有没有明显的内存泄漏点比如静态集合类只增不减、流式处理未关闭、大对象缓存无过期策略等。这套排查思路才是JVM题真正想考察的能力。2.3 Hadoop核心机制HDFS读写的完整生命周期Hadoop生态部分是整套题的压舱石。考得最多的就是HDFS写入流程和MapReduce执行原理。HDFS写入流程标准答案分七步客户端向NameNode发起写入请求NameNode检查权限和文件是否存在返回可用的DataNode列表客户端将文件分块默认128MB客户端向第一个DataNode建立Pipeline连接数据以Packet默认64KB为单位流式传输同时第一个DataNode复制给第二个、第二个复制给第三个每个DataNode写完后逐级返回ACK全部写完客户端通知NameNode关闭文件。这里有一个容易被忽略的细节HDFS是“数据先落盘再返回成功”还是“写完即返回”答案是最后一个DataNode落盘成功并逐级返回ACK后客户端才认为写入成功。这种设计保证了数据的持久性但代价是写入延迟较高。MapReduce执行原理最常考的是Shuffle阶段。Map端的Shuffle包括输入分片→Map函数→环形缓冲区默认100MB达到80%时溢写→分区排序→合并溢写文件。Reduce端的Shuffle包括拉取Map输出→合并排序→分组→Reduce函数。整个过程中环形缓冲区的设计最精妙——它通过“写入和溢写并行”的方式让Map函数不需要等待磁盘IO大大提升了吞吐量。但代价是如果Map输出量突然暴涨缓冲区不够用就会触发阻塞反而拖慢整个作业。我遇到过不少线上问题最终排查下来都是Map输出过大导致Spill次数过多磁盘IO成为瓶颈。解决办法是调大io.sort.mb现在叫mapreduce.task.io.sort.mb或者优化Map端输出尽量少写不必要的数据。YARN的调度器也是常客。FIFO、Capacity Scheduler、Fair Scheduler三者的区别以及生产环境怎么选。我的建议很简单多租户共享集群用Capacity Scheduler需要保证多个业务线资源隔离的场景首选它Fair Scheduler适合任务多而杂、希望小作业也能快速拿到资源的场景。但不管用哪种都要注意配置资源上限避免某个业务线把集群资源占满拖垮其他任务。2.4 简单题背后隐藏的深水区这套题里有一些看起来“送分”的题比如“RDD是什么”“Spark和MapReduce有什么区别”但想拿满分其实没那么容易。RDD弹性分布式数据集这道题如果只答“只读的、分区的、可容错的分布式数据集”只能拿一半分。另一半藏在“弹性”二字里RDD通过血缘关系Lineage实现容错当某个分区数据丢失时可以根据血缘关系重新计算而不需要像MapReduce那样重新提交整个作业。这一点是Spark相比MapReduce的核心优势之一也是后续调优的基础——你理解了血缘才能在设计程序时有意避免过长的血缘链因为一旦某个环节出错重算代价会很高。Spark和MapReduce的区别常规答法是“内存计算vs磁盘计算、DAG vs 两阶段、延迟低vs吞吐稳”。但我要提醒一个容易忽视的点Spark虽然快但在数据量极大且内存不足的场景下会退化为频繁的磁盘Shuffle甚至比MapReduce还慢。所以面试时如果能主动提到“Spark的优势建立在充足内存的前提下实际选型要根据数据规模和资源情况决定”会显得你对这两种引擎的理解更加立体。再来看看Spark的宽窄依赖和Stage划分。这道题几乎是Spark岗位的必考题。窄依赖是指父RDD的每个分区最多被一个子RDD分区使用典型操作是map、filter宽依赖是指父RDD的每个分区可能被多个子RDD分区使用典型操作是groupByKey、reduceByKey、join。Stage的划分依据就是宽依赖——遇到宽依赖就切一刀前面是一段后面是另一段。宽依赖必然触发Shuffle所以调整算子比如用reduceByKey代替groupByKeymap的核心逻辑就是尽可能减少Shuffle的数据量。3. 数据采集与数据仓库设计业务场景题才是拉分项3.1 Flume与Kafka日志链路的两根支柱货拉拉这套笔试题的第三模块个人认为是整套题最有含金量的部分因为它直接考察了你有没有真正搭过数据链路。有一道题大致是在Flume采集日志到Kafka的过程中如何保证数据不丢失。这道题的满分答法要分三端来说。Source端Flume的Source接收数据后如果要写入的Channel满了有两个选择——给上游返回异常让上游重发或者自己落盘到本地磁盘待Channel腾出空间后再写入。生产环境一般用后者即配置File Channel或Memory Channel加磁盘溢出策略。用Memory Channel吞吐高但重启会丢数据用File Channel可靠但性能差一些这个取舍要根据业务容忍度来定。Channel端确保数据落地后再返回ACK不盲目追求吞吐。我记得有一个线上事故某团队把Memory Channel的容量调得很大以为可以扛住流量高峰结果Flume进程OOM重启积压在内存里尚未写入Kafka的数据全部丢失。从那以后我所在的团队统一改用File Channel并且将checkpoint目录和数据目录分开挂载防止磁盘写满导致Checkpoint失败。Sink端Kafka收到数据并写入分区后Flume才认为发送成功。如果Kafka短暂不可用Sink要有重试机制而不是直接把数据丢掉。还有一道题问Kafka的副本机制。核心知识点是Kafka通过分区副本实现高可用每个分区有一个Leader和多个Follower生产者和消费者只与Leader交互Follower定期从Leader拉取数据并写入本地日志。ISRIn-Sync Replicas是Kafka保证数据一致性的关键——只有与Leader保持同步的副本才会被纳入ISR如果某个副本长时间落后会被踢出ISR当Leader宕机时Kafka会从ISR中选举新的Leader。这里有一个容易混淆的点生产者确认机制acksall时是要所有ISR都写入成功还是所有副本都写入成功答案是所有ISR写入成功注意不是所有副本。明白了这一点你就知道为什么需要合理设置min.insync.replicas参数——太大会降低可用性太小则“acksall”形同虚设。3.2 数据仓库维度建模星型模型和拉链表的实战选择数仓设计题考察的核心就是维度建模方法论。最常见的是问星型模型和雪花模型的区别与选型。星型模型是事实表直接连接维度表冗余高但查询快雪花模型是维度表继续规范化拆分冗余低但查询需要关联更多表。生产环境绝大多数场景推荐星型模型原因很简单大数据场景下存储成本相对低但计算成本很高用少量存储冗余换取查询时更少的关联这笔买卖非常划算。还有一道经典设计题如何设计订单事实表。考察点在于事实表分为事务事实表、周期快照事实表和累积快照事实表。订单场景一般是事务事实表每产生一条订单就插入一条记录但如果要分析订单状态变化比如从下单到支付到发货到签收就需要累积快照事实表一行记录包含整条生命周期的多个时间字段。这两种设计没有绝对的对错关键看业务需求——如果你要分析的是订单漏斗转化率用累积快照事实表会更方便。拉链表的设计也很常见。拉链表是缓慢变化维SCD的一种实现通过start_date和end_date两个字段记录一条记录的有效期。当数据发生变化时不更新原记录而是将原记录end_date更新为当前时间插入一条新的有效记录。我在货拉拉系类场景中拉链表用得最多的是司机维表和车辆维表——司机手机号变了、车辆类型换了、所属车队调整了这些都是典型的缓慢变化维。拉链表的代价是查询时要带时间条件过滤写起来稍微麻烦但换来的是完整的历史回溯能力对于需要审计和追溯的业务场景这个设计值得坚持。3.3 实时计算Storm和Spark Streaming在2018年的较量2018年那道关于实时计算选型的题用今天的眼光看已经有了完全不同的答案。当年主要对比的是Storm、Spark Streaming和Flink而现在Flink已经是大数据实时计算领域的事实标准Storm基本被淘汰Spark Streaming也处在被Structured Streaming逐步替代的阶段。这道题的核心考点是实时计算的两种模型微批处理和真正的事件驱动流处理。Spark Streaming是微批Flink是事件驱动。微批的优点是吞吐高、容错简单缺点是延迟受限于批大小且对事件时间的支持天然存在困难事件驱动流的优点是延迟低、支持精确一次语义缺点是状态管理和容错更复杂。2018年那会儿很多团队还在用Spark Streaming因为Flink生态还不成熟但到了今天新项目如果再问我选型建议我会毫不犹豫推荐Flink——除非团队完全没有Flink经验且业务对低延迟没有硬性要求。4. 手写SQL与编程题这两道题写不出前面全白搭4.1 一道窗口函数题连续登录天数SQL题里出镜率最高的就是“求连续登录天数”。经典题目是给定用户登录表user_id, login_date求每个用户连续登录的最大天数。这个题的思路分三步。第一步给每个用户的登录日期按时间排序用row_number()生成序号第二步用登录日期减去序号得到一个辅助日期同一用户连续登录的记录这个辅助日期是相同的第三步按用户和辅助日期分组统计每组记录数就是连续登录天数再取最大值。SELECT user_id, MAX(continuous_days) AS max_continuous_days FROM ( SELECT user_id, grp_date, COUNT(*) AS continuous_days FROM ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL rn DAY) AS grp_date FROM ( SELECT user_id, login_date, ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY login_date) AS rn FROM user_login ) t1 ) t2 GROUP BY user_id, grp_date ) t3 GROUP BY user_id;这个SQL在2018年的笔试题里算是中等偏上难度放到今天依然是高频考点。它考察的核心是窗口函数的理解和“连续”这个语义的转化能力。如果你能把这个解法讲清楚面试官基本会认为你的SQL功底是过关的。4.2 一道业务报表题留存率计算留存率是互联网数据分析里最基础的指标也是SQL题的常客。题目一般是给定用户活跃表user_id, active_date求某一天新增用户在未来第7天的留存率。留存率的标准定义是某日新增用户中在指定日期后第N天仍有活跃的用户占比。SQL实现分两步先找出某日新增用户集合再左连接N天后仍有活跃记录的用户。WITH new_user AS ( SELECT user_id FROM user_active WHERE active_date 2024-01-01 GROUP BY user_id ) SELECT COUNT(DISTINCT ua.user_id) / COUNT(DISTINCT nu.user_id) AS retention_rate FROM new_user nu LEFT JOIN user_active ua ON nu.user_id ua.user_id AND ua.active_date DATE_ADD(2024-01-01, INTERVAL 7 DAY)实际业务中留存率计算没有这么简单。难点在于用户活跃表可能包含一天多次活跃的记录需要提前去重留存要区分“自然日留存”和“自然周留存”不同业务的用户口径可能不一样有的要用设备ID有的要用手机号有的要用账号ID。如果面试答卷上能主动提一句“用户id需要和业务方确认口径避免一个用户多端登录导致重复计算”这题的回答质量会明显高一个档次。4.3 编程题TopN问题的两种境界编程题是TopN问题给定一个大文件每行一个整数求最大的100个数。这题有两种解法对应两种境界。第一种是全部加载到内存排序取前100。时间复杂度O(nlogn)空间复杂度O(n)。如果文件不大这个解法没问题。但问题的关键在于“大文件”三个字暗示了内存装不下。第二种是使用一个大小为100的最小堆遍历所有数据每次和数据堆顶比较如果比堆顶大就替换堆顶并调整堆。时间复杂度降为O(nlog100)空间复杂度降为O(100)。在Java中可以用PriorityQueue实现几行代码就能搞定。public ListInteger top100(int[] nums) { PriorityQueueInteger minHeap new PriorityQueue(100); for (int num : nums) { if (minHeap.size() 100) { minHeap.offer(num); } else if (num minHeap.peek()) { minHeap.poll(); minHeap.offer(num); } } return new ArrayList(minHeap); }如果面试官进一步追问“文件远大于单机内存怎么办”就需要答分治和归并将大文件切分为多个小文件分别求每个小文件的Top100再对这些Top100做多路归并。这实际上就是MapReduce思想在单机环境下的体现。能答到这里说明你对大数据的核心方法论——分而治之——有真正的理解。5. 常见问题与备考建议这套题背后的长期价值5.1 考生最容易犯的三个错误我结合多年面试经验总结了做这类大数据笔试题最常见的三个问题。第一是答原理时只会背结论不会举例子。比如“HDFS写入流程是七步”但你问“如果第三个DataNode写入失败会怎么样”很多人就卡壳了。答案应该是Pipeline会中断已经写入成功的DataNode会保留数据客户端会重新选择一个DataNode将剩余数据写入新的Pipeline。这说明读源码和静态背答案之间隔着很大的距离。第二是数仓设计题只答概念不落场景。问你“如何设计订单表”你列了事实表和维度表的定义但没有结合题目里给的业务背景说清楚粒度、分区策略、更新策略。面试官要的是“你在这个业务里会怎么建表”而不是教科书上的定义。我的建议是准备工作时找自己项目中的两张核心表把设计过程完整复盘一遍包括为什么字段这么放、为什么按天分区、为什么用拉链表能把这一套讲明白胜过背十种模型。第三是SQL题不注重可读性写出巨复杂的嵌套子查询。实际上面试官看SQL题时除了关心结果对不对还很在意你的SQL是不是清晰可维护。能用CTE分开写的就别全部嵌在一起能加注释的就加注释。这一方面是代码规范另一方面也反映了你在团队协作中的沟通意识。5.2 备考知识图谱从真题到体系最后给正在准备大数据岗位笔试的读者一份系统的备考清单按优先级排序Java基础与并发HashMap原理、并发包常用工具CountDownLatch、Semaphore、ConcurrentHashMap、线程池参数与拒绝策略、JVM内存模型与GC算法。优先级高因为这是第一轮筛人的门槛。Hadoop生态HDFS读写流程与容错机制、MapReduce Shuffle原理、YARN调度器区别与选型。优先级高这是大数据开发的看家本领。Spark核心RDD特性与血缘、宽窄依赖与Stage划分、Spark Shuffle原理、常用调优手段内存比例、序列化、数据倾斜处理。优先级高这是当前离线计算的主流工具。数据采集Flume组成与Source/Channel/Sink配置、Kafka分区副本机制与消费者组模型、两者之间如何保证数据一致性。优先级中高因为这是数据链路的起点。数仓理论星型模型与雪花模型、事实表三种类型、拉链表设计、分层架构ODS/DWD/DWS/ADS。优先级中高业务型数据团队非常看重。SQL与算法窗口函数和与操作、留存/漏斗/复购等业务指标SQL、TopN与分治思想。优先级最高这是每天要用的基本功。备考时切忌贪多求全。大数据岗位的知识体系太庞大了面面俱到的结果是每个点都浅尝辄止。我的建议是先保证核心知识达到能讲透原理的程度再围绕自己的项目案例把一两条链路完整跑通。举个例子你完全可以用一个日志分析项目作为主线从Flume采集、Kafka传输入手用Spark做ETL再写SQL落到数仓层最后产出报表。这条链路走通一遍笔试里至少60%的内容你都能从容应对。5.3 写在最后的一点感想回到货拉拉这套2018秋招笔试题。如果你现在翻开它可能会觉得有些知识点“过时了”——比如Storm已经被Flink取代MapReduce越来越少直接使用。但换个角度看这套题其实是很好的历史切片它记录了大数据技术栈从批处理为主、实时为辅到实时计算全面崛起的过渡阶段。而像HDFS的容错设计、Shuffle的原理、数仓维度建模的方法论这些底层的东西不管上层框架怎么迭代始终没有变过。我在实际带团队的过程中越来越确信一件事框架可以学得快但数据工程的基本功——对数据链路各个环节的理解、对数据质量的掌控、对业务指标的计算逻辑——才是数据工程师的核心竞争力。笔试只是入场券真正决定你能走多远的是你能不能在生产环境里稳住一条又一条数据链路让它们每天准确、及时、完整地流转。
返回列表