恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
大数据面试高频考点全攻略:HDFS、Kafka、Flink与数据倾斜调优
首页
资讯中心
/
大数据面试高频考点全攻略:HDFS、Kafka、Flink与数据倾斜调优
大数据面试高频考点全攻略:HDFS、Kafka、Flink与数据倾斜调优
发布时间:2026/10/13 18:12:07
简介2023年史上最全的大数据面试题定位为大数据开发、运维、云计算、数据治理及架构师岗位的通用备考资料内容横跨Linux与Shell、Hadoop、Zookeeper、Flume、Kafka、Hive、HBase、Sqoop、Scala及Spark十大专题既包含常用命令、端口配置、读写流程等基础题也深入Shuffle过程、数据倾斜、参数调优、血统机制等高频难点适合不同层级的求职者系统复盘。资料以单个docx文档呈现压缩包总大小491KB目录按章节组织知识点分条解答便于快速定位和打印背诵。截至目前已有880人浏览学习是一份集中度较高的面试题库整理。读者可借此梳理大数据技术栈的关键考点对照目录查漏补缺尤其能补足项目经验类提问的应答思路。1. 大数据面试题资源一份覆盖全技术栈的硬核复盘做大数据开发最焦虑的时刻不是线上集群出故障而是面试官突然从 Kafka 问到 Flink 状态后端再从 Hive 窗口函数跳到 JVM 内存模型。我拆完这份 2023 版大数据面试题库后发现它几乎把大数据开发、运维、云计算、数据治理、架构师五个方向的高频考点全收进来了从 Linux/Shell 基础到 Hadoop 生态组件再到 Spark、Flink、数据中台和手写 HQL每一章都是按真实面试追问逻辑整理的。对正在准备跳槽的从业者来说这份资源相当于把几年的踩坑经验浓缩成了一份可背诵、可复现的考点地图。我的建议是别把它当普通文档翻把它当成一份模拟面试提纲按章节逐条自测卡住的地方就是你最需要补的短板。2. 存储与计算核心HDFS、MapReduce 和 Yarn 的底层机制2.1 HDFS 读写流程与副本放置策略HDFS 读流程和写流程几乎是每场大数据面试的开场题。写流程的完整链路是客户端调用 DistributedFileSystem.create()向 NameNode 发起 RPC 请求NameNode 检查权限和配额后返回可以写入的 DataNode 列表客户端按 64KB 或配置的 chunk 大小逐块写入每个 block 写完后由 Pipeline 机制传递给下一个 DataNode全部副本写完后返回确认。面试官通常会追问副本放置策略默认策略是第一个副本放在客户端所在节点第二个副本放在与第一个不同机架的节点第三个副本放在与第二个同机架的不同节点这样兼顾了容错和跨机架带宽消耗。读流程的关键在于客户端会先向 NameNode 获取文件的 block 位置列表然后按照网络距离就近读取。这里一个容易被追问的细节是HDFS 的读流程是 client 直接与 DataNode 建立连接读数据不走 NameNode 转发所以 NameNode 不会成为数据读写的瓶颈。数据倾斜在 HDFS 层面的表现是某些 DataNode 磁盘使用率远高于平均值通常原因是小文件过多或者客户端写入时没有调用 setReplication 和 balance 工具。# 执行 HDFS 数据均衡阈值设为 5%带宽限制 20MB/s hdfs balancer -threshold 5 -D dfs.datanode.balance.bandwidthPerSec20971520参数说明-threshold 5表示当节点磁盘使用率与集群平均值的偏差超过 5% 时才触发迁移带宽限制参数的单位是字节/秒20971520 对应 20MB/s生产中建议根据业务低谷期带宽余量调整避免影响在线读写。2.2 MapReduce Shuffle 流程与优化手段Shuffle 是 MapReduce 的灵魂也是面试中连环追问的重灾区。从 Map 端看每个 Mapper 的输出会先写入环形缓冲区默认大小 100MB当缓冲区使用率达到 80% 时触发溢写溢写文件内部会先按分区号排序再按 key 排序最后合并成最终的 Map 输出文件。Reduce 端拉取数据时按照分区号从各个 Map 输出文件拉取属于自己的那部分数据先放内存缓冲区不够就溢写到磁盘全部拉完后再进行一次归并排序然后交给 Reduce 函数处理。// 设置 Map 端环形缓冲区大小与溢写阈值 conf.set(mapreduce.task.io.sort.mb, 256); conf.set(mapreduce.map.sort.spill.percent, 0.85);逻辑说明第一行把缓冲区从默认 100MB 调到 256MB能有效减少溢写次数但过大可能导致 GC 压力增加第二行把溢写阈值从 0.8 提到 0.85缓冲区利用率更高适合 Map 阶段计算较重、内存相对宽裕的场景。需要记住的是Shuffle 的优化核心就一句话减少溢写、减少合并、减少网络传输。Hadoop 优化里还有一个高频考点是小文件处理。小文件过多会让 NameNode 内存吃紧且每个小文件都是一个 blockMap 任务数被撑大集群资源被无效任务占满。常见做法是采用 CombineTextInputFormat 将多个小文件合并成一个 split或者在上游用 Shell 脚本定期合并。压缩方面生产环境中推荐 LZO 或 Snappy配合索引文件可以支持切片而 gzip 虽然压缩率高但不支持切片大文件用 gzip 会导致单个 Map 任务处理时间过长。这是面试官最爱埋的陷阱之一回答时一定要区分清楚。2.3 Yarn 的 Job 提交流程与调度器对比Yarn 的提交流程是区分「背过答案」和「真正理解」的试金石。标准流程是客户端调用 job.waitForCompletion()向 ResourceManager 提交作业RM 启动一个 ApplicationMaster 并分配容器AM 再向 RM 申请资源拿到资源后启动 Task 容器Task 运行完 AM 向 RM 注销。面试官追问点通常在 AM 挂了怎么办、RM 挂了怎么办——AM 挂了可以重新启动RM 挂了需要依赖 Zookeeper 的 HA 机制。实际生产排障时查看作业卡在哪个阶段可以通过yarn application -status查看 AM 状态再结合 ResourceManager 的日志定位。# 查看指定 application 的状态与诊断信息 yarn application -status application_1699999999999_0001命令说明这条命令会返回作业的 state、finalStatus、追踪 URL 以及诊断信息当作业反复失败时诊断信息里通常直接给出失败原因比如队列资源不足、AM 内存超限等比翻日志效率高很多。调度器方面FIFO、Capacity Scheduler、Fair Scheduler 三者的区别是必须背熟的点。FIFO 适合单用户场景先到先服务容易产生队头阻塞Capacity Scheduler 按队列划分资源队列内部是 FIFO适合多租户场景是当前大多数生产集群的默认选择Fair Scheduler 是公平调度资源不足时每个任务分到大致相等的资源量适合多用户共享集群的场景。面试真题里出现过“你们集群用的是什么调度器为什么这么选”这类开放题结合自己公司的实际选型回答远比背概念更有说服力——没有标准答案只有是否适合业务场景。2.4 Hive 核心考点窗口函数与数据倾斜Hive 是离线数仓的柱石面试覆盖面极广但真正拉开差距的是窗口函数和数据倾斜处理。窗口函数的考点集中在 ROW_NUMBER()、RANK()、DENSE_RANK() 的区别以及 SUM() OVER() 这类累加窗口的用法。这里一个高频手写题是“求每个部门工资排名前三的员工”本质是考查开窗后过滤需要用子查询包一层再取排名。-- 求每个部门工资排名前三的员工相同的工资取并列名次 SELECT department_id, employee_name, salary FROM ( SELECT department_id, employee_name, salary, RANK() OVER(PARTITION BY department_id ORDER BY salary DESC) AS rk FROM employee_info ) t WHERE rk 3;逻辑说明内层查询使用 RANK() 按部门分区、工资降序排序生成排名外层过滤掉排名大于 3 的记录。RANK() 遇到相同工资会并列且占用后续名次1、2、2、4如果希望不占用名次1、2、2、3改用 DENSE_RANK()。这是面试中极易被追问的差异点。数据倾斜在 Hive 面试中几乎是必考题。表现是某个 Reduce 任务卡住不动其他任务早跑完了。原因常见于 group by 的 key 分布不均、join 的关联字段大量重复、count(distinct) 对超多 key 去重。解决思路有过滤异常 key、用随机前缀打散、两阶段聚合等具体方案我会在第 5 章详细展开因为这是大数据面试里最值得花时间的考点之一。3. 实时计算链路Kafka 与 Flink 的高频追问3.1 Kafka 可靠性ISR、幂等性与事务机制Kafka 面试题的密集程度在所有组件里排第一。最核心的链路是生产者发消息到 BrokerBroker 落地日志并同步副本消费者从分区拉取数据并提交 offset。面试官最爱从“Kafka 丢不丢数据”切入这个问题必须分层回答生产者端用 acksall 确保消息被所有 ISR 副本接收Broker 端设置 min.insync.replicas2 防止单个副本故障导致数据丢失消费者端先处理业务逻辑再提交 offset且关闭自动提交。ISR 是 In-Sync Replica 的缩写指与 Leader 保持同步的副本集合滞后过多的副本会被剔除等到追上来再重新加入。# 生产者配置核心参数Python 示例 producer_config { bootstrap.servers: kafka-01:9092,kafka-02:9092, acks: all, retries: 3, enable.idempotence: True, linger.ms: 10, batch.size: 65536 }参数说明acksall保证所有 ISR 副本写入成功才返回retries3应对网络抖动但一般配合幂等使用防止重试导致消息重复enable.idempotenceTrue让 Broker 端按序列号去重保证生产者重试不产生重复消息linger.ms10和batch.size65536是吞吐量与延迟的折中前者表示最多等 10ms 凑批后者表示批次大小上限。Kafka 的幂等性只保证单分区内不重复跨分区的事务才需要 Kafka 事务机制。事务的核心原理是 Transaction Coordinator 配合 LSOLast Stable Offset管理事务状态消费者只能读取已提交事务的消息。面试追问一般是“幂等性和事务的区别”回答要点是幂等性针对生产者重试导致的重复事务针对多分区原子性写入两者是不同层面的保证。消息积压是生产环境最常见的故障。处理优先级是先评估积压量用kafka-consumer-groups.sh --describe查看 lag如果积压不严重直接扩容下游消费者实例数注意消费者数不能超过分区数如果积压严重需要临时停掉部分非核心消费者集中资源处理积压同时排查下游处理链路是否有慢操作。Kafka 的高效读写则要从顺序写入磁盘、页缓存、零拷贝三个点解释缺一不可。3.2 Flink 状态与 Exactly-once 语义Flink 相比 Spark Streaming 的核心优势是真正的流式计算、原生状态管理和精确一次语义。面试中第一个追问往往是“Flink 怎么保证 Exactly-once”回答必须分两点状态一致性靠 Checkpoint 分布式快照端到端一致性靠 Kafka 事务型 Sink 或两阶段提交。Checkpoint 的原理是 Barrier 机制Barrier 随着数据流注入每个算子收到 Barrier 就快照自己的状态所有算子完成快照后生成一个完整的 checkpoint下游消费者从最近一次成功的 checkpoint 恢复。// Flink 开启 Checkpoint 并配置参数Java 示例 StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.enableCheckpointing(60000, CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().setMinPauseBetweenCheckpoints(30000); env.getCheckpointConfig().setCheckpointTimeout(120000); env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);参数说明第一行每 60 秒触发一次 checkpoint使用 EXACTLY_ONCE 模式setMinPauseBetweenCheckpoints(30000)表示两个 checkpoint 之间至少间隔 30 秒避免检查点过于密集setCheckpointTimeout(120000)表示单个 checkpoint 超时 2 分钟则丢弃setMaxConcurrentCheckpoints(1)保证同一时刻只有一个 checkpoint 在做防止状态写入冲突。生产环境还要设置后端存储例如配置 RocksDB 或 HDFS。Flink 的状态存储是另一个连环追问点。Managed State 分为 Keyed State 和 Operator StateKeyed State 绑定 key 粒度Operator State 是算子粒度。状态后端有三种MemoryStateBackend 存 TaskManager 内存适合调试FsStateBackend 存 TaskManager 内存HDFScheckpoint 存远端RocksDBStateBackend 本地磁盘存储适合超大状态。面试官通常问“你们状态多大有几十 G 怎么办”答案只能是 RocksDB 配增量 checkpointMemory 和 Fs 后端都撑不住大状态。Flink 的反压机制也是高频题原理是生产速率大于消费速率时通过 Netty 底层的信用协议反馈给上游上游自动降速。这比 Spark Streaming 的背压更彻底因为 Storm 的反压是直接停掉上游而 Flink 是动态调节不会丢数据。回答时如果能对比三者反压机制的差异面试官会明显认可。3.3 Spark Streaming 与 Flink 的架构对比Spark Streaming 消费 Kafka 有两种方式Receiver 模式和 Direct 模式现在生产环境基本只用 Direct 模式因为 Receiver 模式会把 offset 存 Zookeeper而 Direct 模式直接从 Kafka 拉数据、offset 由 Spark 自己管理保证不丢数据。Spark Streaming 的背压机制是从 Spark 1.5 引入的通过 rateController 动态计算最大消费速率参数是 spark.streaming.backpressure.enabledtrue同时设置 spark.streaming.kafka.maxRatePerPartition 作为上限。需要特别注意两者的设计哲学差异Spark Streaming 是微批处理把流切成一个个小批次Spark 2.3 之后引入 Continuous Processing 模式但生产使用较少Flink 是事件驱动程序每来一条数据就处理一条。这个差异直接决定了延迟指标——Spark Streaming 的延迟在秒级Flink 在毫秒级。面试里问到选型时我的回答是如果对延迟不敏感且有大量 RDD/DataSet 批处理逻辑复用选 Spark Streaming如果在乎端到端延迟、需要复杂事件处理和状态管理选 Flink。没有绝对的最优只有适不适合场景。4. 数据倾斜排查与调优必须掌握的实战技术4.1 数据倾斜的表现与定位方法数据倾斜是分布式计算面试出场率最高的技术点原因是它跨越 Hive、Spark、Flink 三个框架且生产场景几乎必现。倾斜的表现很直观某个 Task 长时间运行其他 Task 早就结束整个 Job 等这一个任务或者某个 Task 的输入数据量远大于其他 Task。Spark 里定位倾斜的代码很简单在 Spark UI 中看各个 Stage 的任务耗时分布找到耗时异常高的 Task再查看它的输入数据量和所属的 partition 编号。# 从 Spark UI 的 Stage 页面导出任务耗时数据按耗时降序排列 grep Task app.log | awk {print $5, $13} | sort -k2 -rn | head -20命令说明grep Task app.log筛选出任务日志行$5是任务 ID$13是任务耗时不同版本日志格式略有差异以实际为准sort -k2 -rn按耗时降序排列取前 20 条。这是在没有可视化 UI 时的快速定位法生产环境更推荐直接看 Spark UI 的 SQL 执行计划找到 Shuffle 阶段输入数据量异常的节点。定位到倾斜 Task 后还需要找出导致倾斜的 key。常见做法是在代码中临时加一个抽样统计逻辑先采样一部分数据用 groupBy 统计 key 出现的次数找出次数明显偏高的 key。如果倾斜 key 是空值或 null过滤掉即可如果是业务 key 本身分布不均比如城市维度少数城市占了大头就需要用打散策略。4.2 六大解决方案聚合优化、广播变量与随机前缀应对数据倾斜不能只背概念要能说清楚在什么场景下用什么方案。第一个场景是 group by 聚合导致倾斜解法是两阶段聚合——先给 key 加随机前缀局部聚合再去掉前缀二次聚合。第二个场景是 join 导致倾斜如果小表不大用广播变量把维度表广播到每个 Executor避免 Shuffle。第三个场景是过滤掉异常 key比如某个 key 是脏数据直接过滤掉。# Spark 两阶段聚合解决 group by 倾斜Python 示例 from pyspark.sql import functions as F from pyspark.sql.types import StringType import random def add_prefix(key): return f{random.randint(0, 99)}_{key} # 第一阶段加随机前缀局部聚合 df_with_prefix df.withColumn(prefixed_key, F.udf(add_prefix, StringType())(F.col(key))) partial_result df_with_prefix.groupBy(prefixed_key).agg(F.sum(value).alias(partial_sum)) # 第二阶段去掉前缀二次聚合 final_result partial_result.withColumn( original_key, F.expr(substring(prefixed_key, instr(prefixed_key, _) 1)) ).groupBy(original_key).agg(F.sum(partial_sum).alias(total_sum))逻辑说明先给 key 随机加上 0 到 99 的前缀把原本集中在同一个 key 上的数据打散到最多 100 个子 key局部聚合后去掉前缀再次聚合。随机前缀的位数决定打散程度前缀范围太大比如 0 到 999会导致局部聚合文件过多太小比如 0 到 9又达不到打散效果一般结合数据量和倾斜程度试出来的经验值在 50 到 200 之间。Join 倾斜的另一个思路是 key 分拆把倾斜 key 拆出来单独处理不倾斜的数据走正常 join倾斜的数据加随机前缀后与膨胀 N 倍的维度表 join。这个方案逻辑复杂但效果最彻底。Spark SQL 的 Broadcast Join 阈值默认 10MB小表超过 10MB 就不会自动广播可以手动调整spark.sql.autoBroadcastJoinThreshold参数或者加 broadcast hint 强制指定。-- 强制使用广播 Join避免大表 join 小表时 Shuffle 造成倾斜 SELECT /* BROADCAST(dim) */ fact.order_id, dim.dim_name FROM fact_table fact JOIN dim_table dim ON fact.dim_id dim.dim_id;SQL 说明/* BROADCAST(dim) */是 Spark SQL 的 hint 语法强制 dim_table 走广播变量不参与 Shuffle。使用时注意 dim_table 必须能被完整加载到 Executor 内存否则会 OOM一般几百 MB 以内且压缩后体积可控的维度表才适合。4.3 Spark 常用参数调优与基准测试方法Spark 参数调优面试时不能只报参数名要说出参数调整的前后效果。spark.executor.memory决定单个 Executor 内存默认 1G生产环境一般 4G 到 8Gspark.executor.cores决定 Executor 的 CPU 核数spark.sql.shuffle.partitions决定 Shuffle 后默认分区数默认 200数据量小时要调低数据量大时要调高这是最容易被忽视的参数之一。基准测试在调优前必须做先记录调优前作业的总耗时、每个 Stage 的耗时、GC 时间和 Shuffle 数据量调整参数后再跑一遍对比差异。没有基准的调优都是玄学。# 提交 Spark 作业时的核心参数配置 spark-submit \ --master yarn \ --deploy-mode cluster \ --executor-memory 8G \ --executor-cores 4 \ --num-executors 20 \ --conf spark.sql.shuffle.partitions400 \ --conf spark.sql.autoBroadcastJoinThreshold104857600 \ --class com.example.DataProcess job.jar参数说明--executor-memory 8G搭配--executor-cores 4单个 Executor 并发 4 个任务时每个任务约 2G 内存--num-executors 20总并发 80 个任务spark.sql.shuffle.partitions400根据总数据量调整避免分区数过少导致单分区数据量过大spark.sql.autoBroadcastJoinThreshold104857600表示广播阈值从默认 10MB 调到 100MB100MB 的维度表可以直接广播。这里有一个非常容易踩的坑spark.executor.memory调大后需要同时调整spark.executor.memoryOverhead默认是 executor 内存的 10%如果作业出现 Container killed by Yarn for exceeding memory limits大概率是 overhead 不够。Yarn 会同时计算 executor 内存与 overhead 内存的总和超过容器限制直接 kill这个错误在日志里很容易被误判为数据倾斜或代码 bug实际只是内存配置不足。5. 大数据面试避坑五个高频翻车点与排查经验5.1 端口号与配置文件细节答不上来的基础分现象面试官问 Hadoop 的默认端口号很多人只记得 50070说不出 9870、8088、19888 这些端口分别对应什么服务。 原因日常开发用浏览器访问不关心端口和服务的对应关系配置都是脚本写好的没自己敲过。 解决把端口号整理成一张表按组件分类记忆——NameNode 的 Web UI 端口是 9870Hadoop 3.xResourceManager 的 Web UI 是 8088HistoryServer 是 19888DataNode 是 9864。Hadoop 3.x 相比 2.x 端口变化较大面试时要主动说明自己用的是哪个版本避免答非所问。同样core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml四个配置文件各自管理什么内容必须能脱口而出。5.2 数据一致性问题的回答陷阱只答对一半现象面试官问“Kafka 怎么保证数据不丢失”很多人只回答生产者端设置 acksall消费端完全不提或者只知道关闭自动提交。 原因对 Kafka 的可靠性记忆停留在某一个环节没有形成“生产端-服务端-消费端”三层的完整链路认识。 解决回答时按三层链路组织生产端 acksall retries 幂等性Broker 端 replication.factor 3 min.insync.replicas 2消费端手动提交 offset 先处理后提交。每一层都有各自的丢失风险单层保障是不够的。还有一个加分项是主动提到 ISR 机制的副本同步判定标准——落后 leader 超过replica.lag.time.max.ms默认 30 秒的副本会被移出 ISR这个细节能明显拉开差距。5.3 Hive 内外表选择一句“看情况”丢分现象面试官问内部表和外部表的区别及使用场景有人只答“外部表删数据不删元数据”追问使用场景就说不出来。 原因只背了结论不理解两种表的应用场景差异导致开放题回答得像背书。 解决要结合数仓分层回答。ODS 层用外部表因为原始数据需要保留即使表结构逻辑删除底层文件也要留着DWD/DWS 层用内部表因为都是加工后的数据表删除后数据文件同步清理更合理。另外一个考点是LOAD DATA和INSERT INTO的区别前者是把文件移动到表目录后者是写入新文件面试问“为什么 ODS 层用 LOAD 加载日志”时要回答 LOAD 移动文件不复制速度快且不占用额外磁盘。5.4 Flume 数据丢失Sink 端被忽略的漏洞现象项目里 Flume 从业务服务器采集日志到 Kafka某天 Kafka 集群升级导致 Sink 长时间不可用恢复后发现数据少了约 10 分钟的日志量。 原因Flume 的 Source 端是 TaildirSource支持断电续传但 Kafka Sink 在 Broker 不可写时Channel 被写满后新数据无法写入而 TaildirSource 的断点记录位置是文件末尾之前的位置恢复后从断点重新读但 Channel 中未成功发送的数据已经被清理了。 解决一是把 Channel 从 MemoryChannel 换成 FileChannel提供持久化能力二是给 Kafka Sink 配置合理的batchSize和maxRPCAttempts增加重试次数三是增加 Flume 的 Source 端备份采集服务器上保留原始日志至少 24 小时作为最终兜底。这个坑的教训是Flume 的可靠性是“Source 到 Channel”和“Channel 到 Sink”两个阶段分别保证的任何一个环节都不能省。5.5 手写 SQL 的语法疏漏窗口函数与去重现象手写 SQL 求“连续 3 天活跃用户”很多人知道用 lag/lead 开窗但漏掉了对用户做分组内排序的条件导致结果错乱或者使用 COUNT(DISTINCT ...) 时忘记数据量大时的性能问题。 原因对窗口函数的执行逻辑理解停留在语法层面没有意识到开窗函数内的 PARTITION BY 和 ORDER BY 是两套独立的维度。 解决连续 N 天活跃的核心思路是先用 date_sub 把日期转换为连续性标识再用 row_number 计算用户在分组内的序号日期减序号的差值就是连续分组标记——差值相同的记录属于同一段连续日期。写完后走一遍单用户的数据从头推导验证结果再去写下一题手写 SQL 一笔错全盘错一定要留出检查时间。另外 COUNT(DISTINCT key) 在数据量大时会导致严重的 OOM 或倾斜面试中主动提出改用GROUP BY COUNT(1)或用 approx_count_distinct 近似去重是明显的加分项。6. 手写代码实战HQL 高频题型与 Spark 核心算子6.1 手写 HQL 的四种题型解法手写 HQL 是面试中的压轴环节也是最容易暴露水平的环节。根据这份面试题资源的整理常见题型可以归纳为四类排名类、连续类、留存类、PV/UV 统计类。排名类已经在前文讲过用 RANK/ROW_NUMBER 开窗取前 N。连续类是考察率最高的求连续 3 天活跃、连续 7 天登录都是同一套解题框架。-- 求连续 3 天活跃用户 SELECT user_id FROM ( SELECT user_id, log_date, DATE_SUB(log_date, ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY log_date)) AS group_flag FROM user_active_log WHERE log_date 2023-11-01 AND log_date 2023-11-30 ) t GROUP BY user_id, group_flag HAVING COUNT(1) 3;逻辑说明内层查询先按用户分区、按日期排序用 ROW_NUMBER() 生成序号DATE_SUB(log_date, 序号) 得到分组标记——如果日期是连续的日期减序号的差值不变所以差值相同的记录属于同一段连续日期。外层按用户和分组标记聚合过滤出大于等于 3 天的用户。做题时最容易犯的错是忘记对日期去重同一天有两条活跃记录会导致误判正确做法是先 DISTINCT 或 GROUP BY 去重再做连续性判断。留存类题型的核心是把“当日活跃且 N 天后仍活跃”翻译成 SQL先求出当日活跃用户集合再 join N 天后的活跃数据。PV/UV 统计用 COUNT(1) 和 COUNT(DISTINCT user_id)注意去重用户时数据量大要先用 group by 把 user_id 去重后再 count避免 CPU 和内存双重压力。6.2 Spark WordCount 与算子选型的面试范式手写 Spark WordCount 是最基础但必须写得干净的题。Scala 版本要能一次性写出且不能有明显语法错误Java 版本要能说明 reduceByKey 与 groupByKey 的区别。面试官在 WordCount 之后通常紧接着问“你用过的算子有哪些”然后走到 Shuffle 算子分类和 Shuffle 流程。// 手写 Spark WordCountJava 版本 JavaRDDString lines sc.textFile(hdfs:///data/input); JavaPairRDDString, Integer counts lines .flatMap(line - Arrays.asList(line.split( )).iterator()) .mapToPair(word - new Tuple2(word, 1)) .reduceByKey(Integer::sum); counts.saveAsTextFile(hdfs:///data/output);逻辑说明sc.textFile读取 HDFS 文件得到 RDDflatMap按空格切分单词并摊平mapToPair把每个单词映射成 (word, 1) 的键值对reduceByKey按 key 归约求和。reduceByKey是 Map 端预聚合的算子相同 key 会在 Map 端先合并再 Shuffle数据量远小于groupByKey。Java 版本核心是JavaPairRDD和Tuple2的写法——这是面试手写代码中错误率最高的语法点很多人会在这个细节上卡住。Scala 版本则更简洁用rdd.flatMap(_.split( )).map((_, 1)).reduceByKey(_ _)如果面试官说用 Scala 写就用 Scala 写写完要顺带说一句 reduceByKey 的 Map 端预聚合原理展示的不只是语法能力。6.3 面试中常见的 Scala 考点补漏Scala 考点集中在不可变集合、模式匹配、隐式转换和 Actor 模型。面试中问“伴生对象和普通对象的区别”是最常见的回答是伴生对象与类共享权限可以访问类的私有成员App 入口写在伴生对象里是通过继承 App 特质实现的。偏函数的考点一般结合 match-case 一起问能说出偏函数是只处理部分输入的函数且可以用isDefinedAt检查是否匹配即可。最后的建议是拿到这份题库后不要从头到尾背先按自测法过一遍——合上文档把每个章节标题当作面试题看自己能说出多少。第一遍卡住的内容标记下来第二遍重点攻克第三遍只过标记内容。手写 SQL 和手写代码的部分一定要在编辑器里实际敲一遍眼会了不等于手会了面试时手写代码的习惯和节奏感只有靠真实演练才能练出来。做完这三轮再去面试你会明显感觉到面对追问时的底气不一样了——希望这份资源能帮你把面试准备路上的坑提前填平祝顺利。本文还有配套的精品资源点击获取