2025 年你必须掌握的 15 道数据工程面试题
最常见的数据工程面试题,涵盖 Spark、Kafka、数据建模、管道设计和系统架构——附带详细答案。
数据工程面试很独特——它们融合了编码能力、分布式系统知识和实际架构经验。与偏重算法的 SDE 面试不同,数据工程面试考察的是你是否能设计和构建现实世界的数据基础设施。
以下是 15 道最常见的题目,附带详细答案和准备策略。
数据工程核心概念
1. 数据仓库和数据湖有什么区别?
数据仓库:
- 仅结构化数据
- Schema-on-write(数据在存储前验证)
- 为 SQL 分析优化
- 举例:Snowflake、Redshift、BigQuery
- 使用场景:BI 仪表盘、报表、结构化分析
数据湖:
- 任何格式的原始数据(结构化、半结构化、非结构化)
- Schema-on-read(查询时应用 schema)
- 存储文件(Parquet、Avro、JSON、CSV、图片、日志)
- 举例:AWS S3、Azure Data Lake、GCP Cloud Storage
- 使用场景:ML 训练数据、日志分析、探索性数据科学
湖仓一体(Lakehouse,现代方法): 两者结合——在湖中存储原始数据,同时加入 ACID 事务和查询优化。举例:Delta Lake、Apache Iceberg、Apache Hudi。
面试官为什么问: 他们想看看你是否理解何时使用每种架构,并能讨论各自的权衡。
2. 解释 ETL 与 ELT 的区别
ETL(提取 → 转换 → 加载):
- 在加载到数据仓库之前转换数据
- 传统方法,用于数据仓库计算成本较高的场景
- 工具举例:Informatica、传统 SSIS
ELT(提取 → 加载 → 转换):
- 先加载原始数据,然后在数据仓库内转换
- 现代方法,得益于廉价的云存储和强大的数据仓库计算能力
- 工具举例:dbt(转换步骤)、Fivetran/Singer(提取/加载)
关键洞察: 行业已经大幅转向 ELT。云数据仓库(Snowflake、BigQuery)使得转换成本很低。主要的权衡是治理——ELT 意味着原始(可能杂乱的)数据在你的数据仓库中停留更久。
3. 数据管道中的幂等性是什么,为什么重要?
幂等性意味着多次运行同一个管道与运行一次产生相同的结果。这很关键,因为:
- 管道会失败,需要重试
- 回填历史数据会重新处理已加载的数据
- 在分布式系统中保证精确一次处理很难
如何实现幂等性:
- 使用唯一键(upserts 而非 inserts)
- 按时间窗口分区并覆盖
- 在数据仓库中使用 merge 操作
- 在暂存表中跟踪已处理的事件 ID
大数据框架
4. Apache Spark 如何处理数据分区?
Spark 在内存中(计算期间)和磁盘上(读写时)对数据进行分区。关键概念:
- 默认分区: 集群中的核心数(或 RDD 操作的 200 个分区)
- Repartition: 将所有数据通过网络重新洗牌——代价高昂,谨慎使用
- Coalesce: 减少分区而不进行完全 shuffle——缩小规模时效率高
- Partition by: 写入时按列将数据分组到文件(例如
partitionBy("date"))
常见面试追问: "如果你遇到数据倾斜怎么办?"
回答:一个分区比其他分区接收更多的数据,导致某些任务运行时间更长。解决方案:
- 加盐(给 key 添加随机前缀)
- 自定义分区器
- 对小表使用广播连接
5. 解释 Kafka 与其他消息队列(RabbitMQ、SQS)的区别
| 特性 | Kafka | RabbitMQ | AWS SQS |
|---|---|---|---|
| 模式 | 发布/订阅(事件流) | 消息代理(队列) | 简单队列 |
| 持久化 | 是(可配置保留期) | 可选 | 是 |
| 有序性 | 按分区有序 | 按队列有序 | 尽力而为(FIFO 队列) |
| 回放 | 是(保留消息) | 否 | 否 |
| 吞吐量 | 百万/秒 | 数千/秒 | 数十万/秒 |
| 使用场景 | 事件流、管道 | 任务分发 | 微服务解耦 |
关键洞察: Kafka 专为高吞吐量事件流设计,支持保留和回放。RabbitMQ 在消息路由和复杂模式方面表现优异。SQS 是最简单的,用于基本的解耦场景。
6. 如何在流式管道中处理延迟到达的数据?
延迟数据在实时系统中是不可避免的。策略如下:
- 水位线(Watermarks): 定义一个阈值(例如 5 分钟)——在水位线之后到达的数据被视为延迟
- 允许延迟的会话窗口: 在窗口中处理数据,但允许延迟事件更新结果
- 侧输出(Side outputs): 将延迟数据路由到单独的流进行分析
- 状态化处理: 在内存/磁盘上保持状态以更新之前的结果
- 定期批处理对账: 运行每日批处理作业来纠正任何流式处理的不准确之处
现实案例: 在点击流管道中,网络不佳的移动用户可能延迟 10-30 秒到达事件。5 分钟水位线处理了 99.9% 的情况,每日批处理作业修复剩余部分。
数据建模
7. 什么是维度建模?解释星型和雪花模式的区别。
维度建模(Kimball 方法论)使用以下方式组织数据以支持分析查询:
- 事实表: 定量、可测量的数据(销售额、点击量、交易)
- 维度表: 描述性上下文(产品、客户、时间、地点)
星型模式(Star Schema):
Dim_Time Dim_Customer
\ /
\ /
Fact_Sales
/ \
/ \
Dim_Product Dim_Store
- 事实表直接连接所有维度
- 查询更简单,性能更快
- 维度表中有一些数据冗余
雪花模式(Snowflake Schema):
- 维度表规范化(拆分为子维度)
- 冗余更少,查询更复杂
- 在现代云数据仓库中较少使用(存储成本低)
面试提示: 大多数从业者偏好星型模式,因其简单且查询性能好。雪花模式在理论上更优雅,但实际上更慢。
8. SCD(缓慢变化维)类型 1 和类型 2 的区别?
类型 1(覆盖): 用新值替换旧值。无历史记录。
- 适用场景:历史记录不重要(例如,修正拼写错误)
类型 2(新增行): 创建带有新生效日期的新行。保留历史记录。
- 适用场景:需要跟踪随时间的变化(例如,客户地址变更)
- 需要字段:
effective_date、expiry_date、is_current
类型 3(新增列): 添加"旧值"列。有限的历史记录。
- 适用场景:只需要上一状态,不需要完整历史
真实面试题: "你的分析数据库中,一个客户从免费版升级到专业版,你会如何处理?"
回答:类型 2 SCD——你需要知道每笔交易发生时客户属于哪个层级。
管道设计
9. 为网约车应用设计一个实时分析管道。
需求: 近实时跟踪乘车请求、匹配、完成和收入。
架构:
Mobile App → API Gateway → Kafka (events) → Spark Streaming / Flink
↓
Aggregation (per driver, per zone)
↓
Real-time Dashboard (Redis/Memcached)
↓
Data Warehouse (historical analysis)
关键组件:
- 事件采集: Kafka 主题用于
ride_requested、ride_matched、ride_completed - 流处理: 连接流(将请求与完成匹配)、按司机/区域聚合
- 状态管理: 在状态存储(Redis)中跟踪活跃的乘车
- 输出: 实时指标到仪表盘 + 批量写入数据仓库进行历史分析
- 监控: 在管道延迟、数据质量问题、schema 变更时告警
预期追问:
- 如何实现精确一次处理?
- Kafka 宕机时怎么办?
- 如何处理 schema 演进?
10. 如何确保管道中的数据质量?
采集阶段:
- Schema 验证(检查列类型、必填字段)
- 关键列的空值检查
- 范围验证(例如,时间戳必须在过去)
处理阶段:
- 行数检查(输入 vs 输出)
- 重复检测
- 参照完整性检查
输出阶段:
- 与之前运行对比的摘要统计
- 关键指标的异常检测
- 新鲜度监控(数据到达时间)
工具: Great Expectations、dbt tests、Deequ、自定义验证作业。
系统设计
11. 如何设计一个每天处理 100 亿事件的管道?
关键考量:
- 分区: 按日期 + 小时 + 区域对事件分区,实现并行处理
- 存储: 将原始数据以 Parquet 格式(压缩、列式)存入 S3/GCS
- 处理: Spark Structured Streaming 或 Flink 用于实时处理,Spark 批处理用于每日作业
- 编排: Airflow/Dagster 协调依赖关系
- 扩展: 基于队列深度自动扩展集群
- 成本优化: 容错批处理作业使用 Spot 实例,关键路径使用预留实例
数学计算: 100 亿事件 × 约 1KB/事件 = 约 10TB/天。Parquet 10:1 压缩后,约 1TB 存储数据。100 个执行器的 Spark 集群约 15 分钟处理完毕。
12. 什么是 CDC(变更数据捕获)管道,它是如何工作的?
CDC 实时跟踪数据库变更,无需轮询。
工作原理:
- 读取数据库的事务日志(PostgreSQL 的 WAL、MySQL 的 binlog)
- 解析 INSERT/UPDATE/DELETE 操作
- 将变更流式传输到 Kafka
- 消费变更以更新下游系统
工具: Debezium(开源)、AWS DMS、Fivetran(SaaS)
相比轮询的优势:
- 不增加数据库负载(读取日志成本低)
- 亚秒级延迟
- 精确一次捕获每次变更
数据工程师编码题
13. 编写 Python 函数,按 event_id 去重,保留最新的时间戳
from collections import defaultdict
def deduplicate_events(events: list[dict]) -> list[dict]:
"""
events: list of dicts with 'event_id' and 'timestamp' keys
Returns deduplicated list keeping the latest event for each event_id
"""
latest = {}
for event in events:
eid = event['event_id']
if eid not in latest or event['timestamp'] > latest[eid]['timestamp']:
latest[eid] = event
return list(latest.values())
追问: 如果数据集放不下内存怎么办?
回答:按 event_id, timestamp DESC 排序,然后使用流式方法(只保留最后看到的 event_id)。
14. SQL:找出连续 7 天活跃的用户
WITH active_days AS (
SELECT DISTINCT user_id, DATE(activity_date) as activity_date
FROM user_activity
),
streaks AS (
SELECT
user_id,
activity_date,
DATE_SUB(activity_date, ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY activity_date
)) as streak_group
FROM active_days
)
SELECT user_id, COUNT(*) as streak_length
FROM streaks
GROUP BY user_id, streak_group
HAVING COUNT(*) >= 7;
关键洞察: DATE_SUB 技巧将连续日期转换为同一组。如果日期连续,减去行号会得到相同的结果。
15. 如何优化一个慢速的 Spark 作业?
常见优化技巧:
- 减少 shuffle: 对小表使用
broadcast(),用mapJoin替代join - 合理分区: 按常用过滤列分区,避免过多小文件
- 缓存中间结果: 多次使用的 dataframe 使用
cache()或persist() - 尽早过滤: 在 join 之前下推过滤条件
- 使用合适的文件格式: Parquet 配合列裁剪
- 处理数据倾斜: 加盐、自定义分区器、单独处理热 key
- 用 Spark UI 监控: 识别慢速 stage 和长尾任务
准备策略
第 1-2 周: SQL 进阶(窗口函数、CTE、自连接)——在 DataLemur 上练习 第 3-4 周: Spark 基础 + 动手编码练习 第 5-6 周: 系统设计——练习在白板上设计管道 第 7-8 周: Kafka、消息队列、流式处理概念 第 9 周及以后: 模拟面试,聚焦管道设计场景
资源推荐
- 书籍: Martin Kleppmann 的《Designing Data-Intensive Applications》(必读)
- SQL 练习: DataLemur、StrataScratch
- Spark: Databricks 免费社区版
- 系统设计: Alex Xu 的《System Design Interview》(第 2 卷涵盖数据系统)
总结
数据工程面试考察的是你思考规模、可靠性和权衡的能力。你不需要掌握每一个工具——你需要理解它们背后的原理,并能够设计在生产环境中工作的系统。
准备好和经历过真实面试流程的数据工程师一起练习了吗?联系我们,我们会评估你的优势并制定针对性准备计划。