简介:这份2023年整理的大数据面试题文档,面向大数据开发、运维、云计算与数据治理等岗位的求职者和进阶学习者,围绕Linux与Shell、Hadoop、Zookeeper、Flume、Kafka、Hive、HBase、Sqoop、Scala、Spark等梳理高频考点。压缩包内共1个docx文件,约491KB,内容以目录化文档呈现,便于按模块检索复习。目前已有884人学习下载。文档覆盖端口配置、HDFS读写流程、MapReduce Shuffle、YARN提交流程,以及Kafka数据丢失与重复、Hive数据倾斜、HBase RowKey设计、Spark架构与作业提交流程、宽窄依赖和Shuffle机制,并包含参数调优、项目经验与面试问答思路,适合查漏补缺、模拟面试和搭建知识框架。
1. 大数据面试到底考什么:从 HDFS 写流程这道“送分题”说起
大数据面试题里最容易被当成送分题的,是 HDFS 写流程。候选人张口就是“客户端发请求、NameNode 返回 DataNode 列表、然后 pipeline 写”,面试官追问一句“中间某个 DataNode 挂了,ack 队列怎么处理、副本谁去补”,场面立刻冷下来。这才是这场面试的真实难度分布:大数据开发、大数据运维、云计算、数据治理四条线共用一套底座考点,考的不是你听过多少名词,而是能不能把一条链路从入口讲到落盘、从参数讲到故障兜底。
拿分的顺序很清楚:先啃组件链路(Hadoop、Flume、Kafka),再啃数仓口径(Hive、分层、拉链表),最后用 Spark/Flink 的调优和数据倾斜把分差拉开。这份笔记适合已经能跑通集群、但一被追问就卡壳的人,也适合大数据运维想转开发、需要补齐数仓和计算引擎叙事的人。下面按考点模块一层层拆,每章都配了能直接背的答法和能直接练的题。
2. 组件底座:Hadoop、Flume、Kafka 三条链路必须讲到落盘
这一章是整场面试的基础部分。面试官在这里不指望你背文档,他要判断的是:你有没有真的在集群上出过问题。所以每条链路都按“先讲正常路径,再讲参数,最后讲故障和兜底”来准备。三个组件的考法高度相似——先问流程,再问参数,最后问“数据会不会丢”,你把这三段串成一条线,答案就不会散。
2.1 HDFS 写流程:七个步骤、两个参数、一个故障兜底
标准答法按顺序说,不要省步骤。客户端调用 create 向 NameNode 请求创建文件,NameNode 校验目标文件是否已存在、父目录是否有权限,返回能否上传;客户端请求第一个 Block 的存放位置,NameNode 按机架感知返回一组 DataNode;客户端创建输出流,向第一台 DataNode 发起写请求,这台再调用下一台,逐级建立 pipeline 后逐级应答;客户端开始以 packet 为单位写入,packet 进 dataQueue,同时复制一份进 ackQueue 等确认;每台 DataNode 写完往下传,最末一台应答后逐级回传,客户端收到 ack 才把 packet 从 ackQueue 移除;一个 Block 写完关闭当前流,再申请下一个 Block;全部写完通知 NameNode 关闭文件。
参数就两个最好记:dfs.blocksize默认 128MB,dfs.replication默认 3。packet 默认 64KB,packet 内部再切成 512 字节的 chunk 做校验,这个细节答出来,面试官基本认定你读过源码或至少翻过文档。
故障兜底是分水岭。答法是:pipeline 中某台 DataNode 写失败,客户端靠 ack 超时感知,把故障节点从 pipeline 中剔除,用剩下的节点重建 pipeline 继续写,同时 NameNode 标记该副本不足,后续按副本因子补足。注意别说“客户端自己复制一份”,复制是 NameNode 触发的,不是客户端。
提示:面试官问“读流程”时,重点在“就近读取”和“短路读”。答完 Block 列表按网络距离排序后,补一句“如果客户端和 DataNode 在同一台机器,可以走短路读,省掉网络开销”,这一句就是加分项。
读流程反过来讲:客户端向 NameNode 请求文件,NameNode 返回该文件部分或全部 Block 列表,列表里带每个 Block 的副本所在节点,按距离排序;客户端选最近的节点建流读取,读到的数据先落本地缓存再交给上层;一个 Block 读完关流,再读下一个;全部读完关闭输入流。追问点通常在“读到一半 DataNode 挂了怎么办”——客户端会换下一个副本节点重试,并上报 NameNode 这个副本有问题。
2.2 Yarn 提交流程与三种调度器,别只会说“先申请 Application”
Job 提交流程的完整链条是:客户端调用提交接口,向 ResourceManager 申请 Application ID;RM 返回 ID 和资源提交路径;客户端把 jar 包、切片信息、配置信息传到该路径;客户端向 RM 正式提交 Application,RM 把它放进调度队列;某个 NodeManager 领到任务,创建 Container 并启动 MRAppMaster;MRAppMaster 初始化作业,读取切片信息,为每个 MapTask 申请 Container;MRAppMaster 与对应 NodeManager 通信启动任务;各任务向 MRAppMaster 汇报状态,MRAppMaster 再向 RM 汇报;全部完成后 MRAppMaster 注销自己,RM 回收资源。
三种调度器用一张表答清楚,比背段落稳:
| 调度器 | 分配方式 | 优点 | 典型短板 |
|---|---|---|---|
| FIFO | 按提交顺序先到先得 | 实现最简单,单团队独占集群够用 | 大作业会长期占满资源,小作业被阻塞 |
| Capacity | 预先划成多个队列,每队列给定容量比例,队列内先到先得 | 多部门共享集群,资源边界清晰 | 队列空闲时资源不会自动让给别的队列(需开抢占) |
| Fair | 按权重动态分配,运行中的作业都能分到大致均等的资源 | 小作业响应快,适合交互式、混合负载 | 作业多时频繁切换,单作业吞吐不如前两者 |
面试题常这么问:“你们集群用哪种调度器,为什么?”答题思路是先把业务形态说出来——如果集群是多部门共用、要保证每个部门有保底资源,Capacity 更合适;如果是分析型任务多、大小作业混跑、要求小作业不能饿死,Fair 更合适。别只答名字,要带一句“我们当时开没开抢占、队列怎么划分”。
2.3 Flume 的 Put/Take 事务与“采集会不会丢数据”
Flume 的组成先说清:Source、Channel、Sink 三件套加一个 Agent 进程,Source 负责对接数据源,Channel 做缓冲,Sink 负责往下游送。真正拉开差距的是两个事务。
Put 事务发生在 Source 到 Channel 之间:Source 把一批数据先写进临时缓冲区 putList;提交前检查 Channel 剩余容量是否够放这批数据;够就提交,数据进 Channel,对外可见;不够就回滚,putList 清空,Source 稍后重试。Take 事务发生在 Channel 到 Sink 之间:Sink 从 Channel 取一批数据放进 takeList;提交成功后这批数据才真正从 Channel 移除;如果 Sink 往下游写失败,回滚,takeList 里的数据归还 Channel。
“采集会不会丢数据”这题,答题分三层。第一层看 Channel:Memory Channel 靠内存缓冲,进程挂掉数据就没了;File Channel 把事件写磁盘并维护 checkpoint 和索引,进程重启能恢复,代价是吞吐下降。第二层看 Source:普通 Tail Source 不记录读取位置,重启只能从新数据开始;Taildir Source 会把每个文件的读取位置写进 positionFile,支持断点续传,这是生产上更稳的选择。第三层看 Sink:HDFS Sink 写入过程中的临时文件、批次大小、滚动策略没配好,会出现半截文件或大量小文件。
小文件这块有固定答法:控制hdfs.rollInterval(按时间滚动)、hdfs.rollSize(按大小滚动)、hdfs.rollCount(按事件数滚动)三个阈值,再配合调大批次、适当延长滚动周期,把“每个文件都很小”变成“文件大小接近 Block 尺寸”。面试官听到你报出这三个参数名,基本不会再追。
注意:Flume 的事务只保证“Source→Channel→Sink 这一段内部不重复不丢失”,跨到下游存储的精确一次,要下游自己配合幂等或事务,不要把话说满。
2.4 Kafka 的 acks、ISR 和积压处理
“Kafka 丢不丢数据”是必考题,答案要分生产端和消费端。
生产端看acks:设为 0,发出即认为成功,leader 挂了就丢;设为 1,leader 写入成功即返回,leader 在同步给 follower 之前挂掉会丢;设为 -1(即 all),要求 ISR 内所有副本都写入才返回,最安全。配合min.insync.replicas(常用 2)和replication.factor(常用 3),意思是“至少 2 个副本确认才算写成功”,即使挂一台机器也不丢。
ISR 这个概念必须能解释:ISR 是与 leader 保持同步的副本集合,判断标准是副本的 LEO 落后 leader 不超过replica.lag.time.max.ms(默认 30 秒),超时就被踢出 ISR。HW 是高水位,等于 ISR 中所有副本 LEO 的最小值,消费者只能消费到 HW 之前的数据,这样 leader 切换时不会读到未同步的消息。被踢出 ISR 的副本追上进度后可以重新加入。
消费端丢数据是另一套逻辑:关闭自动提交、改成处理完再手动提交 offset,能避免“还没处理就提交了”导致的丢失;重复消费则靠幂等处理解决——写数据库用主键或唯一索引,写 Hive 用分区覆盖,别指望 Kafka 帮你做业务去重。Kafka 自身的幂等性靠enable.idempotence=true,用 PID 加序列号在单会话单分区内去重;跨分区跨会话的精确一次要用事务,配transactional.id走两阶段提交。
积压处理这题按“先止血再治本”答:短期加消费者实例,但实例数不要超过分区数,超出的部分空转;同时调大max.poll.records、把单条处理逻辑异步化或批量化,提高单条消费吞吐;如果是持续性增长,评估扩容分区(注意按 key 有序的场景扩容后会打乱顺序),或者把非核心 topic 降级处理。分区数估算给个粗口径:目标写入吞吐除以单分区可承受吞吐,单分区按 5~10MB/s 保守估,再留 30% 余量。
3. 数仓与 Hive:分层、拉链表和手写 HQL 是同一套逻辑
数仓部分决定你是“会写 SQL 的开发”还是“懂建模的数仓工程师”。面试官在这里最爱干两件事:让你把分层讲清楚,然后现场出一道手写 HQL。分层讲不清,后面调优和治理全是空话;HQL 写不出来,前面说得再漂亮也会被打折。这章按“分层 → Hive 语法 → 建模 → 手写”的顺序推进。
3.1 数仓分层与 ODS、DWD、DWS 各层到底做了什么事
分层要讲出“为什么分”,不是背名字。ODS 层贴源,基本不做清洗,保留原始数据以便回溯,压缩和存储格式要选得省空间(常见是 ORC 或 Parquet 加 Snappy);DWD 层做清洗和规范化——去空、去重、脱敏、维度关联、统一字段命名和枚举值,把杂乱日志变成明细事实;DWS 层按主题和粒度做轻度汇总,比如按天按用户、按天按商品;ADS 层直接面向报表和应用,指标口径在这里固化。
面试题喜欢问“DWD 层做了哪些事”,答题模板是四件事:清洗(过滤脏数据、异常值、测试流量)、规范化(字段统一、码值统一、时间格式统一)、维度补全(关联用户维度、商品维度、地理维度)、分流(把不同业务线拆成不同事实表)。再补一句“DWD 一般保持最细粒度,不做聚合,聚合放到 DWS”,逻辑就闭环了。
“一天跑多少张表、跑多久、哪张表最费时间”这种题,考的是你有没有真的盯过调度。答法给数量和量级,再指出最慢的那张通常是明细大表关联多张维表,解决方式是提前过滤、维表广播、拆分任务并行跑。
3.2 Hive 内部表外部表、4 个 By 与窗口函数
内部表和外部表的区别一句话说透:删表时内部表连元数据带 HDFS 数据一起删,外部表只删元数据,数据还在。使用场景上,原始数据、多组件共享的数据用外部表,中间结果和临时表用内部表。回答时补一句“生产上 ODS 层基本都是外部表,就是为了防止误删”,比较有说服力。
4 个 By 是高频送分题,但答错的人不少:
| 关键字 | 作用范围 | 是否全局有序 | 常用场景 |
|---|---|---|---|
| order by | 全局 | 是 | 结果集小,需要全局排序 |
| sort by | 单个 reduce 内部 | 否 | 每个 reduce 内部有序,总体不保证 |
| distribute by | 控制数据分发到哪个 reduce | 否 | 配合 sort by 使用,按字段分桶处理 |
| cluster by | distribute by + sort by | 否 | 分发字段和排序字段相同且只能升序 |
窗口函数的答法是“结构 + 三个常用函数”:over(partition by ... order by ... rows between ... and ...)定义窗口范围,row_number给同组内唯一序号,rank遇并列跳号,dense_rank遇并列不跳号。举一个真实会问的场景:每个品类取销售额前 3 的商品,用row_number() over(partition by 品类 order by 销售额 desc)再筛序号小于等于 3 就行。
自定义函数也常问:UDF 是一进一出,UDTF 是一进多出,UDAF 是多进一出。答的时候带一句“临时函数用 create temporary function 注册,只在当前会话有效;永久函数要注册到元数据里”,能体现你实际用过。
3.3 拉链表与同步策略:变化的数据怎么留痕
拉链表解决的是“数据会变,但历史状态要能查”。表结构一般两个日期字段:start_date表示这条状态生效日期,end_date表示失效日期,当前有效的数据end_date填一个极大值如9999-12-31。
更新逻辑分两步:先把当天有变化的记录在旧表里闭合,也就是把原记录的end_date改成变更日期的前一天;再把当天的新状态插入,start_date是变更日期,end_date是极大值。查询当前状态加where end_date = '9999-12-31',查询某个历史日期切片用where start_date <= '2023-06-01' and end_date >= '2023-06-01'。
同步策略要能说出适用条件:全量同步简单但每次全表拉,适合小表;增量同步只拉变化数据,需要有可靠的更新时间字段;新增及变化同步是在增量基础上把更新也捕获到,通常靠update_time加当天分区;特殊情况下用触发器或日志解析。答题时把“表的数据量、是否有可靠时间戳、下游要不要历史状态”三个判断条件说出来,比背四种策略名字有用。
3.4 手写 HQL:连续 N 天活跃这类题的固定套路
这类题几乎是必考,套路只有一句:把“连续”变成“日期减序号等于常数”。先按用户和日期去重,再用row_number打序号,用日期减去序号得到分组标记,连续日期会落在同一个标记里,最后按标记分组计数。
-- 求最近 30 天内连续活跃 3 天及以上的用户 select user_id, min(dt) as start_dt, -- 连续区间的起始日 max(dt) as end_dt, -- 连续区间的结束日 count(1) as continuous_days from ( select user_id, dt, -- 关键:连续日期的 dt 与序号差值恒定,形成同一个分组标记 date_sub(dt, row_number() over (partition by user_id order by dt)) as grp from ( select user_id, to_date(create_time) as dt from user_action_log where dt between '2023-01-01' and '2023-01-30' group by user_id, to_date(create_time) -- 先按天去重,一天多条只算一次 ) t1 ) t2 group by user_id, grp having count(1) >= 3;逻辑说明:最内层做去重,保证一个用户一天只留一条,这一步漏掉就会把“一天刷了三次”算成连续三天,是最常见的翻车点。中间层用date_sub(dt, 序号)把连续日期压成同一个grp值,日期断开时grp自然变化。最外层按user_id和grp分组,用having卡住天数阈值。
参数说明:把dt换成实际分区字段,create_time换成实际时间列;要查“连续 7 天”只改having的阈值;要查“最近连续 3 周活跃”,把按天去重换成按周去重(用weekofyear或者用固定起始日做floor(datediff(dt,'2020-01-06')/7)),其余逻辑不动。
同一套思路还能答“沉默用户”“回流用户”“流失用户”。沉默用户是最近 N 天没有活跃但之前活跃过;回流用户是之前活跃、中间断了 N 天、最近又活跃;流失用户是连续 N 天完全没活跃。三者的区别只在时间窗口的取法,答题时把窗口边界说清楚,比堆 SQL 更得分。
4. Spark 与 Flink:从宽窄依赖问到反压
计算引擎这章是拉分项。面试官的判断标准很直接:你能不能说清一个作业从提交到跑完发生了什么、哪里会 shuffle、哪里会 OOM。背算子名单意义不大,能把 stage 划分、shuffle 类型、缓存与 checkpoint 的取舍讲明白,才算过关。Flink 则重点考三层图、Slot 与并行度、反压和精确一次。
4.1 Spark 架构与作业提交流程:从 submit 到 task
架构先摆清楚:Driver 里跑 SparkContext,负责解析作业、生成 DAG、划分 stage、调度 task;Cluster Manager 负责资源分配(Standalone、Yarn、K8s 都算);Worker 上有 Executor,Executor 里跑 task,task 之间共享内存和缓存。
提交流程按顺序说:spark-submit提交应用,客户端向集群管理器申请资源;集群管理器在某台节点上启动 Driver(cluster 模式)或由客户端本地充当 Driver(client 模式);Driver 初始化 SparkContext,向集群管理器申请 Executor;Executor 启动后反向注册到 Driver;Driver 解析代码生成 DAG,划分 stage,把 task 分发到 Executor 执行;task 执行完汇报结果,全部结束后释放资源。
clinet 和 cluster 的区别用一句话点破:client 模式下 Driver 在提交机器上,日志本地就能看,适合调试,但提交机挂了作业就挂;cluster 模式下 Driver 在集群里,适合生产,日志要去集群上捞。
# 生产上常见的提交写法,参数按集群实际资源改 spark-submit \ --master yarn \ --deploy-mode cluster \ --class com.demo.MainJob \ --num-executors 20 \ # Executor 个数,按队列可用资源定 --executor-memory 8g \ # 单 Executor 内存,别超过节点可用的 75% --executor-cores 4 \ # 单 Executor 核数,2~5 比较稳 --driver-memory 4g \ # Driver 内存,涉及 collect 时要留够 --conf spark.default.parallelism=160 \ # 默认并行度,约等于总核数的 2~3 倍 --conf spark.sql.shuffle.partitions=320 \ # shuffle 后分区数,按数据量调 --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \ app.jar参数说明:num-executors × executor-cores决定总并发,一般占到队列可用核数的 60%~80%;executor-memory要留出堆外和系统预留,别把节点内存吃满,否则会被 Yarn 杀掉容器;spark.sql.shuffle.partitions默认 200,数据量大时偏少会拖慢、偏多会小文件泛滥,按每个分区 100~200MB 的量级反推比较实际。
4.2 宽窄依赖、stage 划分与 task 个数
窄依赖是父 RDD 的每个分区最多被子 RDD 的一个分区使用,典型是map、filter、union、mapPartitions。宽依赖是子 RDD 的一个分区依赖父 RDD 的多个分区,必须 shuffle,典型是groupByKey、reduceByKey、sortByKey、distinct、repartition、未预分区的join。
为什么区分这个?容错角度:窄依赖某个分区丢了,只重算对应的父分区;宽依赖丢了,要重算整个父 stage 的所有分区,代价大得多。所以宽依赖处建议配合 checkpoint 切断血缘。
stage 划分答法:从最后一个 RDD 往前推,遇到宽依赖就切一刀,切出一个 stage;最后一个 stage 的分区数决定 task 个数,task 个数又决定并发度。面试题喜欢问“我的作业只有 3 个 task,怎么提并发”,答案就是调repartition或者改spark.default.parallelism,但别盲目调大,分区太多调度开销也上去了。
4.3 reduceByKey 与 groupByKey、缓存与 checkpoint
reduceByKey与groupByKey的区别,核心在 map 端有没有 combine。reduceByKey会先在 map 端对相同 key 做局部聚合,再走网络,传输量小;groupByKey把所有 value 直接拉到 reduce 端再分组,数据量一大就 OOM。答题时补一句“能用 reduceByKey 就别用 groupByKey,聚合类需求还可以考虑 aggregateByKey 或 combineByKey 控制初始值和聚合函数”,比较完整。
repartition与coalesce:repartition本质是coalesce(num, shuffle=true),一定走 shuffle,可增可减,数据分布均匀;coalesce默认不 shuffle,只能减分区,减少分区时可能造成数据倾斜。生产上小文件合并常用coalesce,但要先确认下游不依赖均匀分布。
缓存与 checkpoint 的区别按四个维度答:存储位置,缓存优先内存、内存不够落磁盘,checkpoint 一般写 HDFS;血缘,缓存保留血缘、丢了能重算,checkpoint 切断血缘;生命周期,缓存作业结束或unpersist就释放,checkpoint 持久保留;触发时机,缓存 lazy,遇到第一个 action 才生效,checkpoint 也需要 action 触发。
共享变量也常问:广播变量是只读的,每个 Executor 存一份,用来把小维表发下去做 map 端 join,避免每个 task 复制一份;累加器只能在 Executor 侧累加、在 Driver 侧读结果,适合做计数和异常统计。
4.4 Flink 三层图、Slot 与并行度、反压与精确一次
三层图要能顺下来:代码里的算子构成 StreamGraph;提交时做算子链优化后生成 JobGraph(把能 chain 的算子合并成一个任务);JobManager 再根据并行度把 JobGraph 展开成 ExecutionGraph,每个顶点按并行度拆成多个 ExecutionVertex,这才是真正被调度的单位。
Slot 与并行度的区别:Slot 是 TaskManager 上的资源子集,做内存隔离,一个 TaskManager 有几个 slot 就能跑几个并行的 task;并行度是算子本身的并发实例数。两者关系是一个 slot 可以跑同一任务链上的多个算子,但总并行度受 slot 总数限制。面试题问“并行度怎么设”,答法是按数据量和下游承压能力定,source 端并行度对齐 Kafka 分区数,中间算子按 CPU 核数配,窗口和聚合算子适当调大,sink 端按目标库写入能力压。
反压的答法分版本:早期基于 TCP 的滑动窗口,下游处理慢会通过 TCP 反压把上游堵住;新版改成基于 credit 的流量控制,接收端先告诉发送端自己还有多少缓冲区可用,发送端按信用发送,避免盲目堆积。排查时看 Web UI 的反压面板,红色表示该节点被下游堵住,再往上游找第一个红色节点。
精确一次要讲清两件事:checkpoint 时 barrier 在流里插入,算子收到 barrier 做本地快照,barrier 对齐后上报完成,JobManager 汇总成全局一致的 checkpoint;sink 端通过两阶段提交,在 checkpoint 完成时统一提交外部事务。所以 Exactly-once 的实现 = 状态快照 + 两阶段提交 sink,缺一不可。
窗口与水印也是高频:滚动窗口不重叠、滑动窗口有步长、会话窗口按活跃间隔切分;水印用来衡量事件时间的进展,watermark = 观察到的最大事件时间 - 允许乱序时长,窗口在水印越过结束时间时触发。答的时候补一句“允许乱序时长设太大会延迟高,设太小会丢迟到数据,交易类场景一般留几秒”,很有实战味。
5. 易错点:这四类答法最容易失分
这一章的每一条都是从考场里捞出来的。不是你不会,而是答的方向偏了,面试官听三句就给你画个叉。每条按“错在哪、为什么错、怎么答对”三段来处理,照着改一遍,同样的知识点得分能差一档。
5.1 数据倾斜:只说“加随机前缀”
错在哪:一被问数据倾斜,直接甩“给 key 加随机前缀打散”,再问就没了。
为什么错:加盐只是 join 场景的一种解法,而且它要求另一侧能对应展开,答不出适用条件就等于背了个名词。面试官想听的是定位过程和解法选择。
怎么答对:按四步说。先讲表现——大部分 task 很快跑完,少数 task 长时间卡在 99%,或者某个 executor 频繁 GC、OOM;再讲原因——join 的 key 分布不均、group by 的维度值过度集中、空值参与关联、分区键选得不合理;然后讲定位——看 Spark UI 的 stage 里 task 耗时分布、抽样统计热点 key 的占比;最后才讲解法,且要分场景:聚合类用两阶段聚合(先加随机前缀局部聚合,再去掉前缀全局聚合),join 类把小表广播出去做 map 端 join,大表对大表用热点 key 单独抽出来处理,空值导致倾斜就先给空值打散再过滤。
5.2 数据丢失与重复:只答“我开了事务”
错在哪:问 Kafka 会不会丢数据、Spark Streaming 能不能精确一次,答“我们开了事务”就结束。
为什么错:事务是手段不是结论,面试官要的是“哪个环节可能丢、你用什么机制堵住、代价是什么”。
怎么答对:按链路上每一段拆。生产端看acks和min.insync.replicas,broker 端看副本因子和是否允许非 ISR 副本选主,消费端看 offset 提交时机;计算端如果是 Spark Streaming,要说明“手动管理 offset + 业务处理和提交放在同一个事务里,或者下游写入做幂等”;如果是 Flink,要说明 checkpoint 加两阶段提交 sink。最后补一句代价:精确一次换来的是延迟上升和吞吐下降,对账类业务必须付这个代价,日志类业务允许至少一次加去重就够了。
5.3 指标口径:把“留存”答成一段 SQL
错在哪:问“怎么分析用户 1 天留存”,上来就写 SQL,写完没有一句解释。
为什么错:面试官先要看你对口径的理解,SQL 只是实现。口径说不清,写得再顺也是错的。
怎么答对:先说定义——“1 天留存是指某天新增的用户,在次日仍然活跃的比例”;再说口径细节——“新增按注册时间还是首次启动时间、活跃按启动还是按关键行为、跨天怎么算”;然后给计算思路——新增表按日期取用户集合,活跃表取次日用户集合,两者按用户 ID 求交集再除以新增数;最后才写 SQL。多花十秒讲口径,比多写十行 SQL 值钱。
5.4 项目经验:把架构答成组件清单
错在哪:问“介绍一下你的项目”,回答“我们用了 Hadoop、Hive、Spark、Kafka、Flink、HBase”,然后没有然后了。
为什么错:组件清单谁都会背,面试官想知道的是数据从哪来、怎么流、你在里面解决了什么。
怎么答对:按“数据源 → 采集 → 存储 → 计算 → 服务”讲一条链路,每一段说清用的什么、为什么选它、量级多大。比如采集段说日志和业务库两条线分别怎么进、峰值多少;计算段说离线跑批和实时链路各自承担什么指标;服务段说结果落到哪、给谁用。最后一定要挂一个自己解决的问题——某个任务原来跑 4 小时,通过调分区数和小文件合并压到 40 分钟,这类具体数字比任何形容词都有说服力。
6. 答题技巧:链路、参数、兜底,三步把答案说满
考场上真正拉开差距的不是知识量,是答题结构。同样一个知识点,有人答三句就没了,有人能撑三分钟且每句都在点上。我自己的习惯是把任何一道题都塞进三步:先说正常链路,再报关键参数,最后讲故障兜底。这三步对应面试官的三个判断——你懂不懂原理、有没有真用过、出事能不能扛。
| 步骤 | 说什么 | 常见题上的落点 |
|---|---|---|
| 链路 | 按顺序把流程讲完,不跳步 | HDFS 写流程、Yarn 提交流程、Flink 三层图 |
| 参数 | 报出两三个关键参数名和默认值/常用值 | dfs.blocksize、acks、spark.sql.shuffle.partitions |
| 兜底 | 出故障怎么办、代价是什么 | pipeline 节点挂掉、消费积压、数据倾斜 |
链路这一步最容易被跳过,很多人上来就讲优化,面试官连你的基础流程都没听到,后面的优化全是空中楼阁。参数这一步是可信度的来源,报得出参数名说明你调过,报不出就只能停在“我了解”的层面。兜底这一步决定你能不能从“及格”走到“优秀”,因为生产环境天天出事,能讲清怎么救的人才算真上手。
还有一个实用技巧:被问到不会的,别硬编。可以说“这块我在生产上没直接处理过,我的理解是按某某机制走的,如果有偏差您指正”,然后把能推的部分推出来。面试官对“不会但会推”的容忍度,远高于“不会还硬编”,后者一旦被追问两层就露馅,前面答得再好也会被打折。
手写题留足时间。HQL 和算法题一共就那么几类,连续 N 天、TopN、行列转换、同环比,背的是模板不是答案。写完主动说一句“这里我先按天去重,是为了防止一天多条记录被误判成多天”,把自己踩过的坑讲出来,比等面试官挑毛病强。
最后一条血泪经验:面试前把项目里的数字全捋一遍——集群多少台、日增数据多少 GB、核心表多少张、最慢的任务多久、优化前后差多少。这些数字你答得出来,整场面试的可信度就立住了;答不出来,讲再多架构都像纸上谈兵。从那以后我每次面试前都强制走一遍这三步和这张数字清单,哪怕准备时间只有半小时。希望帮到你。
本文还有配套的精品资源,点击获取