面灵AI→

米哈游数据开发实习二面:数仓调优与AI落地

轮次
二面
时间
2026-08
来源
牛客网

《面试题目》

  1. 自我介绍。
  2. 你简历提到做主题集市类数据开发,主要负责哪些内容?
  3. 数据开发中部分指标普遍偏高,核心原因是什么?业务侧有哪些优化方案?
  4. 看板中指标波动的评判标准是什么?如何判定波动是否合规?看板的核心作用是什么?
  5. 你是否参与过某主题集市的搭建?搭建思路、数据粒度如何选择?
  6. 数据开发任务性能优化,你做过哪些相关工作?分为哪几个方向?
  7. 资源调优时主要调整哪些核心参数?调参的底层逻辑是什么?
  8. 你参与过 Agent 相关工作吗?主要做什么?有哪些核心方向?
  9. Agent 相关问题深挖(原帖未展开,共两问)。
  10. 你对大模型微调是否了解?有哪些基础认知?
  11. 假设交给你一份全新的数据开发需求,结合 AI 工具落地全流程,你认为有哪些关键环节需要重点考虑?
  12. 你做数据开发覆盖哪些链路?日常开发主要使用什么技术?底层原理学习到什么程度?
  13. 你如何区分宽窄依赖?开发中为什么要尽量规避宽依赖?
  14. 日常开发中任务参数调优的通用思路是什么?平台层面如何配置调参?
  15. 多表 JOIN 关联有哪些优化方案?广播 JOIN 适用场景是什么?
  16. 广播 JOIN 存在什么性能风险?
  17. 你的毕业时间是什么时候?在校课程安排如何?实习规划是怎样的?
  18. 你当前实习工作内容出现了什么变化?为什么在投递新岗位?
  19. 反问:想了解本岗位的发展路径、新人培养模式,岗位分为哪些方向?
  20. 反问:该岗位所属部门日常工作强度、上下班时间如何?是否常态化加班?

《参考解析》

数据倾斜的处理:倾斜的本质是「某个 key 上聚集了远超平均值的数据量」,表现为绝大多数 task 早就跑完、剩几个 task 卡在 99% 长时间不动,或者某个 executor 内存爆掉 OOM。先定位再动手:看 Spark UI 的 task 耗时分布与 shuffle read size 分布,或对 join key / group by key 做采样统计(SELECT key, count(*) GROUP BY key ORDER BY 2 DESC LIMIT 20)确认热点。处理手段按场景选:① 热点 key 单独处理——把大 key 拆出来单独算再 union 回去;② 加盐打散——给 key 拼随机前缀(如 key_0..key_9)先做局部聚合,再去掉前缀做全局聚合,这是两阶段聚合(map 端预聚合 + reduce 端最终聚合)的通用套路,注意只适用于聚合类操作,join 需要两边同时加盐扩展;③ 小表广播(MapJoin)彻底避开 shuffle;④ 开启 map 端聚合(spark.sql.shuffle.partitions 之外的 combine)、调整并行度让单 task 数据量下降;⑤ 大表 join 大表且热点集中在少数 key 时用「热点分离 + 非热点常规 join」;⑥ 数据本身问题——空值/null 全落到一个 reducer,把 null 转成随机值或提前过滤。

资源调优的核心参数与逻辑:调参的目标只有一句话——「让每个 task 的数据量与每个 executor 的并行度匹配,减少 Shuffle 与 spill」。常调参数:executor 数量(spark.executor.instances 或 --num-executors)、单 executor 的核数(spark.executor.cores)与内存(spark.executor.memory)、driver 内存、并行度(spark.sql.shuffle.partitions 或 spark.default.parallelism,经验值是「总核数的 23 倍」,配合 spark.sql.adaptive.enabled 让 AQE 自动合并小分区)、单分区最大字节数(spark.sql.files.maxPartitionBytes,决定读文件时的分片粒度)、内存分配比例(spark.memory.fraction、spark.memory.storageFraction,即执行内存与缓存内存的划分)、shuffle 分区数与压缩、以及倾斜自动处理(spark.sql.adaptive.skewJoin.enabled)。调参逻辑可以归纳成三步:先看瓶颈在哪(CPU 打满?内存 spill?Shuffle 量过大?IO 等待?),再定位是并行度不足还是单 task 过大,最后只动 12 个参数并重跑对比。最忌讳一次改一堆参数,因为无法归因。平台层面通常把这些参数可视化配置(执行器数量、单执行器分片上限、内存占比),这也是「底层原理日常直接用的场景不多」的原因。

宽窄依赖与为什么规避宽依赖:窄依赖是「一个父分区最多被一个子分区使用」,例如 map、filter、union、mapPartitions,父 RDD 的每个分区独立流向一个子分区,不需要跨节点搬数据;宽依赖是「一个父分区被多个子分区使用」,例如 groupByKey、reduceByKey、join(未广播时)、repartition、distinct,需要 shuffle,父分区数据要按 key 重新分发到不同节点。规避宽依赖的原因有四个:一是网络与磁盘开销,shuffle 要把中间数据落盘再跨节点传输,是作业中最贵的环节;二是失败恢复代价,窄依赖只需重算丢失的那个分区,宽依赖要回溯重算多个上游分区(血缘越长越贵);三是数据倾斜与 OOM 大多发生在 shuffle 阶段,因为按 key 分发会导致某些分区远大于其他分区;四是它把执行切成多个 stage,流水线被打断,调度与等待成本上升。优化方向就是「能不加 shuffle 就不加」:用 reduceByKey/aggregateByKey 替代 groupByKey(前者有 map 端预聚合)、用广播 join 替代 shuffle join、用 coalesce 替代 repartition(不产生 shuffle)、提前 filter 减少参与 shuffle 的数据量。

广播 JOIN 的适用场景与风险:适用场景是「小表 join 大表」:把小表(经验阈值是压缩后几十 MB 以内,或 spark.sql.autoBroadcastJoinThreshold 默认 10MB 可调)全量分发到每个 executor,在 map 端完成关联,完全省掉大表的 shuffle,通常能带来数倍提升。风险面很具体:① 每个 executor 都要常驻一份小表副本,小表其实不小(比如上百 MB 或上千万行)时会吃掉大量执行内存,导致 executor OOM 或频繁 GC,此时分发开销反而超过常规 join 的 shuffle 开销;② 广播本身有 driver 收集与网络分发的成本,广播变量过多会拖慢 driver;③ 表统计信息过期会让优化器误判大小而做出错误选择(ANALYZE TABLE 要跟上,或通过 hint 显式控制);④ 广播后无法利用分区裁剪,如果小表其实带分区条件、过滤后很小,先 filter 再广播比直接广播整表更划算。一句话结论:广播 JOIN 的收益取决于「小表有多小 + 分发成本 vs shuffle 成本」,要按表体量实测决定,而不是无条件用。

指标口径与看板波动怎么判定:指标「普遍偏高」通常不是模型算错了,而是口径与业务定义错位,常见根因有:统计口径与业务口径不一致(去重维度不同,比如按订单去重还是按人去重)、时间口径不一致(自然日 vs 业务日、时区、跨天归属)、过滤条件缺失(未剔除测试账号、内部账号、退款/取消单、爬虫流量)、重复计算(上游数据重复、join 产生笛卡尔放大、多次上报未去重)、以及数据迟到与回溯补数造成的「重复叠加」。业务侧的优化方案是先把口径写进指标字典并固化(谁定义、谁评审、什么时间生效、历史数据是否回溯),再对齐数据源与埋点。看板中判断波动是否合规,常用三招:同期对比(同比/环比)、统计控制线(按历史分布算均值 ± 3σ,超出即告警)、以及变点检测与同环比结合;同时要区分「业务真实变化」与「数据管道故障」,做法是给关键指标配数据质量校验(行数波动、主键唯一性、空值率、枚举值越界),异常先查管道再找业务。看板的核心作用是「让业务与数据在同一份事实上对话」:统一口径、发现异常、支撑决策,而不是堆一堆图表。

宽表/主题集市与数据粒度怎么选:搭建主题集市的标准流程是「确定主题域 → 梳理业务过程与维度 → 设计总线矩阵(哪些事实表 × 哪些维度)→ 选粒度 → 定维度与度量 → 落地分层(ODS/DWD/DWS/ADS)」。粒度选择是核心决策,原则是「优先选最细粒度并保证原子性」——最细粒度能支撑后续任意上卷聚合,代价是存储与计算成本;如果业务明确只做粗粒度分析且数据量极大,才退到轻度汇总粒度,并且要在 DWS 层按「维度组合 + 时间窗口」预聚合(如「用户-日」「商品-日」汇总表),ADS 层再做面向看板的宽表。要说明的取舍是:一份明细打天下的做法查询慢、成本高;而上卷太早会丢掉下钻能力,所以常见架构是「明细一层 + 轻度汇总一层」并存,由查询引擎自动选择(或由 BI 语义层路由)。

AI 工具落地数据开发全流程:关键环节可以按流水线讲。① 前期业务口径对齐是重中之重:与业务方书面固化指标定义、计算口径、统计范围与验收样例,因为 AI 自动生成的 SQL 极易出现口径偏差(字段选错、去重维度错、时间窗口差一天),不提前对齐就无法保证准确性;② 标准化开发链路:建表 DDL、字段命名与注释、SQL 编写、单测样例数据生成这类有固定模式的活最适合交给 AI,配合模板与代码库示例能显著提效;③ 任务故障自动化排查:调度失败后让 AI 解析运行日志、定位是数据源延迟、依赖未就绪还是资源不足,并输出故障分析与修复建议,人来确认执行;④ 多层级数据复核校验:建表阶段让 AI 补字段中文释义与业务标签;开发完成后用 AI 对比指标文档与产出表的维度、粒度、行数、枚举值,做一致性评估,减少人工复核量,但最终口径确认仍要人签字;⑤ 人机边界与安全:AI 不应直连生产执行变更,SQL 要走审核与灰度,敏感字段脱敏,所有 AI 生成的逻辑都要有测试用例锁定行为。

Agent 与大模型微调的基础认知:数据开发场景下的 Agent 通常做三件事:把自然语言需求转成 SQL/任务(NL2SQL,配合表结构检索与指标语义层)、调度失败后的自动诊断与修复建议(读日志、比对依赖、给出重跑或调参方案)、数据质量异常的归因(指标波动时自动下钻维度找异常分群)。工程上难点是「可验证性」——SQL 生成后必须能执行并校验结果,否则幻觉无法拦截。微调方面至少要能说清基本概念:全量微调 vs 参数高效微调(LoRA/QLoRA 冻结原权重只训练低秩增量矩阵,显存需求小、可插拔)、指令微调(SFT)与偏好对齐(RLHF/DPO)的区别、微调能改变「行为与格式」但补不了知识盲区(补知识优先用 RAG)、以及数据质量与评测集比调参更决定效果。落到数据开发上,微调的合理用武之地是「让模型熟悉公司内部的表命名规范、SQL 方言与常见模式」,而不是让它记住业务口径(口径应该写进语义层,随时可改)。