订阅更新

迁移项目怎么讲才能体现你的价值:迁移项目表达模板

项目实战表达 13 讲 · 第 5 讲 约 31 分钟读完

本文目录
  1. 开篇:面试官追问的不是技术细节
  2. 先说结论
  3. 迁移项目的 STAR 模板——EMR→Databricks 完整示范
  4. 迁移项目的五个高分表达技巧
  5. 迁移项目核心代码——双写双读的关键实现
  6. 面试官最爱追问的迁移问题
  7. 迁移 vs 调优 vs 数仓——面试选什么项目讲?
  8. 其他高频追问
  9. 面试回答分层——60 vs 75 vs 90 分
  10. 总结
  11. 下一篇

开篇:面试官追问的不是技术细节

我跳槽某短剧出海公司那次,5 轮面试里讲过 4 次EMR 到 Databricks 迁移。

有意思的是——面试官追问最多的不是技术细节。

不是"你的 Spark 调优怎么做的"。 不是"Delta Lake 和 Parquet 有什么区别"。 不是"你用的 Z-Ordering 算法原理是什么"。

面试官追问最多的是——

"你怎么保证这次迁移不出问题?" "如果某个作业迁移后数据不对,你怎么发现、怎么回滚?" "你一个人迁移 100+ 个作业,时间怎么安排的?" "迁移过程中业务方是否无感知?" "你怎么说服老板做这次迁移?"

这些问题和 Spark 原理、Delta Lake 特性完全无关——它们考的是工程成熟度。

这就是迁移项目的价值——它不是"技术深度"的最佳舞台(那是实时项目的事),而是"架构能力 + 项目管理 + 风险控制 + 团队协作"综合能力的最佳舞台。


迁移项目是大数据面试里最能体现工程成熟度的项目类型。一个能独立完成大规模迁移并做到零故障的工程师,几乎默认是靠谱的中高级工程师——因为:

  • "零故障"意味着你有严谨的方案设计——不是撞大运
  • "100+ 作业"意味着你懂项目管理——不是瞎忙
  • "双写双读灰度"意味着你懂风险控制——不是莽干
  • "量化的效果对比"意味着你懂业务价值——不是埋头技术

迁移项目是 Top 30% 工程师才有机会做的项目。如果你做过,一定要讲好。


这篇我会讲透迁移项目的表达模板。用我独立负责的 EMR 到 Databricks 迁移(100+ Spark 作业、3 个月、零故障、ETL -40%、成本 -35%)做完整示范。看完你能做到——同样是迁移项目,别人讲"我参与了迁移",你讲出完整的方案设计、风险控制、数据校验、量化效果。


先说结论

迁移项目讲四个关键点:

  1. 为什么迁(旧方案的痛点、新方案的优势)—— 技术驱动 + 业务驱动,不是"为迁而迁"
  2. 怎么迁(方案设计)—— 双写双读、分批迁移、灰度切换、SQL 方言改写
  3. 风险怎么控制(关键保障)—— 数据校验、回滚方案、分批策略
  4. 效果怎么量化(硬数字)—— 性能/成本/运维三维对比

四个点加起来,才是完整的迁移项目。只讲"怎么迁"不讲其他三个,就是在讲"技术任务"而不是"项目"。


迁移项目的 STAR 模板——EMR→Databricks 完整示范

S(背景,30秒)

"我在某出海硬件品牌负责数据平台。数仓跑在 AWS EMR 上——100+ 个 Spark 作业、日均处理 20 亿行数据、集群规模 50 个节点。

EMR 有三大痛点:

第一,运维复杂。 NameNode 经常不稳定——每月 1-2 次 GC 抖动,抖动期间整个 HDFS 读写变慢,所有作业被拖累。YARN 队列调优也很复杂,要手动配置内存、CPU、并行度。团队只有我一个人做平台,运维压力大。

第二,性能不够。 ETL 经常超 SLA——凌晨 2 点跑的作业,早上 8 点还出不来,业务晨会看不到数据。根本原因是 EMR 的 Spark 生态不够现代化——没有 AQE、没有 Z-Ordering、没有 Delta Lake 的 ACID。

第三,成本高。 EMR 按峰值配置集群,低峰期浪费严重——凌晨 10 个节点就够跑,白天要 50 个,但一直按 50 个常驻。月成本 6.5 万美元,财务年年施压要降本。"

这段 S 的三个亮点:

  • 规模数字具体:100+ 作业、20 亿行/天、50 节点
  • 痛点分 3 类,每个都有根因:运维(NameNode 抖动)、性能(超 SLA)、成本(低峰浪费)
  • 有组织压力:财务施压、业务抱怨——这让迁移不是"个人兴趣"而是"公司需要"

T(任务,20秒)

"我独立负责把整个数仓从 EMR 迁移到 Databricks。目标三个:

1. 零故障完成迁移——不能有业务可感知的数据事故; 2. ETL 执行时间减少 30%+; 3. 运维工作量降低 50%+。"

"独立负责"这个词再次强调归属。三个可量化目标——不是"提升性能"这种模糊目标,是具体的 KPI。

A(行动,1.5 分钟)—— 迁移项目的核心

"核心方案是双写双读 + 分批灰度:

核心设计:双写双读。 不是一刀切停服迁移,是新旧系统并行运行 4 周——ETL 作业同时在 EMR 和 Databricks 上跑,输出到各自的存储。下游报表 API 继续读 EMR 的旧数据(作为主链路),只在后台对比新旧数据差异。确认连续 7 天数据完全一致后才切换下游读 Databricks。这样即使新系统有问题也随时切回旧系统,业务无感知。

分批策略:4 批迁移 100+ 作业。

  • 第 1 批:30 个无下游依赖的清洗作业(风险最低)——用来熟悉 Databricks 平台、发现 SQL 方言差异
  • 第 2 批:40 个 DWD 层作业(风险较低)——基于第 1 批的经验优化迁移 SOP
  • 第 3 批:25 个 DWS/ADS 层作业(风险中等)——开始涉及业务指标
  • 第 4 批:10 个核心指标作业(风险最高)——此时方法论已经成熟,处理最有信心

每批迁移后做三层数据校验:行数精确匹配、关键字段 SUM 误差 < 0.01%、随机抽样 1000 行逐字段对比。校验通过才进入下一批。

SQL 方言改写:大约 20% 的 SQL 有方言差异需要改——日期函数、INSERT OVERWRITE 语法、LATERAL VIEW EXPLODE 等。我整理了《方言差异对照表》供团队同事改 SQL 时查询。

回滚方案:双写期间 EMR 数据完整保留;任何一批迁移失败,调度平台一键切回 EMR 版本。"

A 的四层价值信号:

  1. 核心方法论明确:双写双读
  2. 分批策略有风险考虑:从低风险到高风险
  3. 校验机制完善:三层校验
  4. 回滚可执行:不是口号是机制

R(结果,45秒)

"3 个月完成,零故障。

性能数字:

  • ETL 执行时间从平均 5 小时降到 3 小时,减少 40%(AQE + Z-Ordering + Broadcast Join)
  • 查询延迟降低 70%(Delta Lake 列统计 + Parquet 向量化读)
  • 存储成本降低 35%(Delta Lake Zstandard 压缩 + 生命周期管理)

运维数字:

  • 运维工作量从每周 10 小时降到 4 小时,减少 60%——NameNode 抖动问题归零,YARN 调优不再需要

业务影响:

  • ETL 产出时间从 8 点(超 SLA 2 小时)提前到凌晨 4 点,业务晨会看数据恢复正常
  • 整个迁移方案沉淀成《大数据平台迁移 SOP》,另外 2 个兄弟团队的迁移直接复用
  • 月成本从 6.5 万美元降到 5.5 万美元(含 Databricks License),节省 ~$12K/月"

迁移项目的五个高分表达技巧

技巧 1:强调"零故障"——这是最稀缺的能力

"零故障"是迁移项目的杀手锏。99% 的候选人没做过零故障迁移——要么没做过迁移,要么做过但出过事故。

完整表达:

"迁移最大的风险不是技术,而是稳定性。100+ 个作业、20 亿行/天的数据,任何一点差错都可能影响下游几十个报表、几百个业务方。

我的方案核心是'稳字优先'——不是求快,是求稳:

  • 双写双读:新旧系统并行跑,4 周观察期
  • 渐进切换:4 批迁移,每批至少 2 周观察期
  • 三层校验:行数 / 聚合值 / 抽样明细
  • 回滚预案:每个作业都能在 10 分钟内切回旧版本

整个 3 个月的迁移期间,下游业务报表连续 100% 可用,没有一次数据事故被业务方发现。这就是'零故障'的含义——不是没出过小问题,是所有问题在我手里就解决了,没让业务方感知到。"

"零故障"这句话讲出来的时候,要强调它是"业务可感知的零故障",不是"技术零问题"。 技术上难免有小问题(某个作业迁移后数据不对、某个 SQL 跑挂),关键是在发现和解决链路上没让业务感知——这才是成熟工程师的标志。

技巧 2:讲分批策略的"思考"而不是"动作"

很多人讲分批只讲"我分了 4 批"——面试官听完感觉是机械动作。

加分版本:讲分批的逻辑和考虑。

"不是随机分批,也不是均匀分批,是按风险从低到高排序。

第 1 批选什么——无下游依赖的清洗作业。比如ods_user_login_clean这种,只是把 ODS 的原始数据做一点清洗,下游没有其他作业依赖它。即使迁移后数据错了,也不会污染下游。这一批的作用是'试水'——跑通整个迁移链路、熟悉 Databricks 平台、发现 SQL 方言差异。

第 2 批选什么——DWD 层的一般作业。基于第 1 批发现的问题,修复方言差异对照表、优化迁移 SOP。这一批的作用是'规模化'——从 30 个作业上升到 40 个,验证方法论能不能支撑更多作业。

第 3 批选什么——DWS/ADS 层作业。开始涉及业务指标(GMV、DAU、留存率),风险上升一档。这一批的作用是'验证业务正确性'——跑完要和运营、产品、财务同步核心指标对账。

第 4 批选什么——最核心的指标作业(年 GMV、月度财报)。为什么放最后?因为前三批已经把方法论、工具、经验都打磨好了,到第 4 批时我已经有 90% 的信心。如果第一批就上核心指标,风险根本不可控。

这个分批逻辑的本质是 ——'用低风险作业积累经验、用高风险作业验证成果'。"

这个回答最硬的点——面试官听完会觉得你懂项目管理不只是懂技术。

技巧 3:讲 SQL 方言差异的具体例子

SQL 方言差异是所有跨平台迁移都绕不开的话题——能讲出具体差异细节,是"真做过"的硬标签。

完整表达:

"EMR 是 Hive + Spark on YARN,Databricks 是 Spark + Delta Lake——虽然都是 Spark,但 SQL 方言有很多细节差异,大约 20% 的 SQL 需要改。

最常见的三类差异:

第一类:日期函数签名差异。

-- EMR Hive 写法
SELECT date_sub('2024-01-15', 7);        -- 返回 '2024-01-08'

-- Databricks Spark SQL 写法  
SELECT date_sub(DATE'2024-01-15', 7);    -- 需要 DATE 字面量或 cast
-- 或者改用:
SELECT date_add('2024-01-15', -7);       -- Spark 更推荐

第二类:INSERT OVERWRITE 分区语法差异。

-- EMR Hive 动态分区(需要先 SET hive.exec.dynamic.partition.mode=nonstrict)
INSERT OVERWRITE TABLE dwd_order PARTITION(dt)
SELECT order_id, user_id, amount, dt FROM ods_order;

-- Databricks Spark SQL(默认支持动态分区,但有细节:分区字段必须在 SELECT 最后)
INSERT OVERWRITE TABLE dwd_order PARTITION(dt)
SELECT order_id, user_id, amount, dt FROM ods_order;
-- 如果 Databricks 表是 Delta 格式,推荐用 MERGE 替代 INSERT OVERWRITE:
MERGE INTO dwd_order t
USING ods_order s ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;

第三类:LATERAL VIEW EXPLODE 嵌套结构差异。

-- EMR Hive 炸开嵌套 Array
SELECT user_id, tag
FROM user_tags
LATERAL VIEW EXPLODE(tag_array) t AS tag;

-- Databricks Spark SQL(更推荐内联)
SELECT user_id, explode(tag_array) AS tag 
FROM user_tags;

我整理了一份《方言差异对照表》,把所有踩过的坑和正确写法都列出来,团队里其他同事改 SQL 时直接查表,避免重复踩坑。"

这段回答最硬的点:每个差异都有两段代码对比——这是"真改过大量 SQL"才能讲出的细节。

技巧 4:讲数据校验的方法论

数据校验是迁移项目的生命线——如果迁移后数据不对、还没发现,就是最严重的事故。

完整表达:

# 数据一致性校验脚本(迁移期间每天自动跑)
def validate_migration(table_name, bizdate):
    """三层校验:行数 -> 聚合值 -> 抽样明细"""
    
    # =============== 第一层:行数精确匹配 ===============
    emr_cnt = spark.sql(f"""
        SELECT COUNT(*) FROM emr_db.{table_name} 
        WHERE dt = '{bizdate}'
    """).first()[0]
    
    db_cnt = spark.sql(f"""
        SELECT COUNT(*) FROM databricks_db.{table_name} 
        WHERE dt = '{bizdate}'
    """).first()[0]
    
    if emr_cnt != db_cnt:
        alert(f"{table_name} 行数不一致: EMR={emr_cnt}, DB={db_cnt}")
        return False
    
    # =============== 第二层:关键字段聚合值对比 ===============
    # 从元数据里读每张表配置的'关键校验字段'
    critical_cols = get_critical_cols(table_name)  
    # 示例: fact_order -> [amount, order_count]
    
    for col in critical_cols:
        emr_sum = spark.sql(f"""
            SELECT SUM({col}) FROM emr_db.{table_name} 
            WHERE dt = '{bizdate}'
        """).first()[0]
        
        db_sum = spark.sql(f"""
            SELECT SUM({col}) FROM databricks_db.{table_name} 
            WHERE dt = '{bizdate}'
        """).first()[0]
        
        # 浮点数允许 0.01% 的误差(不同平台浮点计算顺序可能有微小差异)
        if emr_sum and abs(emr_sum - db_sum) / emr_sum > 0.0001:
            alert(f"{table_name}.{col} 聚合不一致: "
                  f"EMR={emr_sum}, DB={db_sum}, "
                  f"diff={(db_sum-emr_sum)/emr_sum:.4%}")
            return False
    
    # =============== 第三层:随机抽样 1000 条明细对比 ===============
    # 用主键抽样,保证两边抽到相同记录
    sample_pks = spark.sql(f"""
        SELECT {get_pk(table_name)} 
        FROM emr_db.{table_name} 
        WHERE dt = '{bizdate}' 
        TABLESAMPLE(1000 ROWS)
    """).collect()
    
    diff_count = 0
    for pk_row in sample_pks:
        pk_val = pk_row[0]
        emr_row = spark.sql(f"""
            SELECT * FROM emr_db.{table_name} 
            WHERE dt='{bizdate}' AND {get_pk(table_name)}='{pk_val}'
        """).first()
        
        db_row = spark.sql(f"""
            SELECT * FROM databricks_db.{table_name}
            WHERE dt='{bizdate}' AND {get_pk(table_name)}='{pk_val}'
        """).first()
        
        # 逐字段对比
        if emr_row != db_row:
            diff_count += 1
    
    if diff_count > 0:
        alert(f"{table_name} 抽样明细差异 {diff_count}/1000 条")
        return False
    
    # 三层都通过,校验 PASS
    log_validation_pass(table_name, bizdate, emr_cnt, db_cnt)
    return True

讲的时候怎么说:

"这个脚本我每天自动跑——迁移期间的 4 周里,每天对 300+ 张表做三层校验:

第一层:行数精确匹配(精度要求最高)——两边行数必须完全一致,差 1 行都告警。

第二层:关键字段聚合值(SUM/COUNT/AVG)——允许 0.01% 的浮点误差(Spark 不同版本的浮点计算顺序可能有微小差异)。每张表预先配置 '关键校验字段'(比如 fact_order 配 amount 和 order_count)。

第三层:随机抽样 1000 条明细——按主键抽样保证两边抽到同一批数据,逐字段对比。这一层能发现聚合值正确但某些字段值错误的问题。

实际上发现过问题:有一次聚合值完全一致,但抽样对比发现 country_code 字段 10% 的值有偏差——EMR 是 'CN',Databricks 变成 'cn'。原因是某个 UDF 在两个平台的默认大小写处理不同。如果没有第三层抽样校验,这个问题会一直没发现。

这就是为什么三层校验缺一不可——每一层都有独特的检测能力。"

技巧 5:讲回滚方案——展示你的"最坏情况准备"

"回滚"是很多候选人不敢讲的话题——觉得讲回滚就是承认"我可能失败"。但面试官恰恰欣赏"敢承认失败 + 有应对方案"的候选人。

完整表达:

"回滚方案的核心逻辑是:双写期间 EMR 数据始终在跑、始终完整保留,回滚只是'把下游读的数据源指回 EMR'——不是技术回滚,是调度切换。

具体机制:

第一,调度层保留双版本。 每个作业在调度平台有两个版本——job_v1_emr 和 job_v2_databricks。正常情况下 v2 跑 Databricks 版本,v1 只观察不触发下游。如果要回滚,把 v2 暂停、v1 激活,下游恢复读 EMR 数据。操作 10 分钟内完成。

第二,数据层保留双份。 EMR 继续跑原作业输出到 S3 原目录,Databricks 输出到 S3 新目录。下游读什么目录是通过配置中心控制的——改一个配置就切换读哪边,不用改下游代码。

第三,报警和切换预案。 数据校验脚本发现异常(比如行数差异>0.1%)会触发自动告警 + 人工决策——告警里直接给出'一键回滚按钮',DBA 确认后点一下就切回 EMR。

实际发生过一次回滚:第 2 批迁移时有个作业用了我们自己写的 UDF,这个 UDF 依赖 Hive 的一个老类,迁到 Databricks 后报 ClassNotFoundException。作业跑失败了,我立刻把这个作业回滚到 EMR 版本,修复 UDF 后第二天重新迁移——整个回滚+修复用了 8 小时,下游业务无感知。

这次回滚不是失败,是成功的风险应对——证明了双写双读方案的价值。没有这个方案,这个 UDF 问题会导致连续几个下游报表数据缺失。"

讲回滚故事最加分的点——真的发生过一次回滚,而且你坦诚承认。 面试官听完不会扣分反而会加分——"这人经历过真实问题,方案是经过实战验证的。"


迁移项目核心代码——双写双读的关键实现

双写机制(一份代码,两个目的地)

# 双写入口 - Spark 作业同时写 EMR 和 Databricks
def dual_write(df, table_name, bizdate, write_mode='dual'):
    """
    write_mode:
      'emr_only'     - 仅写 EMR (迁移前)
      'dual'         - 双写(迁移中)
      'db_only'      - 仅写 Databricks (迁移后)
    """
    
    # 分区配置
    partition_cols = get_partition_cols(table_name)
    
    # ========== 写 EMR (老系统,路径在 S3 老目录) ==========
    if write_mode in ('emr_only', 'dual'):
        emr_path = f"s3://data-lake-emr/{table_name}/dt={bizdate}"
        df.write \
          .partitionBy(*partition_cols) \
          .mode('overwrite') \
          .parquet(emr_path)  # EMR 用原生 Parquet
        log(f"EMR 写入成功: {emr_path}")
    
    # ========== 写 Databricks (新系统,路径在 S3 新目录,用 Delta 格式) ==========
    if write_mode in ('dual', 'db_only'):
        db_table = f"databricks_db.{table_name}"
        df.write \
          .format('delta') \
          .partitionBy(*partition_cols) \
          .mode('overwrite') \
          .option('replaceWhere', f"dt = '{bizdate}'") \
          .saveAsTable(db_table)  # Databricks 用 Delta Lake
        log(f"Databricks 写入成功: {db_table}")
    
    # ========== 双写完成后自动触发校验 ==========
    if write_mode == 'dual':
        validation_result = validate_migration(table_name, bizdate)
        if not validation_result:
            alert(f"[URGENT] {table_name}@{bizdate} 双写数据不一致")

下游读切换(配置中心控制读哪一侧)

# 下游作业读数据的统一入口
def read_table(table_name, bizdate):
    """根据配置中心的开关,决定读 EMR 还是 Databricks"""
    
    # 从配置中心读当前表的读取源
    read_source = config_center.get(f"migration.read_source.{table_name}", 
                                     default='emr')  # 默认读 EMR 保底
    
    if read_source == 'databricks':
        # 新链路: 读 Databricks Delta 表
        return spark.table(f"databricks_db.{table_name}") \
                    .filter(f"dt = '{bizdate}'")
    else:
        # 老链路: 读 EMR Parquet 目录
        emr_path = f"s3://data-lake-emr/{table_name}/dt={bizdate}"
        return spark.read.parquet(emr_path)

# ========== 切换操作(人工执行) ==========
# 灰度切换: 某张表从 EMR 切到 Databricks
# config_center.set("migration.read_source.dwd_order", "databricks")
# 
# 紧急回滚: 发现问题立即切回 EMR
# config_center.set("migration.read_source.dwd_order", "emr")

TCO 对比表(说服老板的武器)

|           成本项          |   EMR (老方案)   | Databricks (新方案) |
|---------------------------|------------------|---------------------|
| 计算集群 (EC2)            | $52K/月          | $35K/月 (Spot 70%)  |
| 存储 (EBS/S3)             | $8K/月           | $5K/月 (分层)       |
| License (Databricks)      | $0               | $15K/月             |
| **总云账单**              | **$60K/月**      | **$55K/月**         |
| ---                       | ---              | ---                 |
| 运维人力 (FTE)            | 0.5 人 (~$5K/月)  | 0.1 人 (~$1K/月)    |
| 故障损失 (业务影响)       | ~$3K/月 (估算)   | 0                   |
| ---                       | ---              | ---                 |
| **TCO 总计**              | **$68K/月**      | **$56K/月**         |
| **月节省**                |                  | **$12K (18%)**      |
| **年化节省**              |                  | **$144K**           |

讲这张表的时候怎么说:

"这是我当时说服老板的 TCO 对比表。核心点是——Databricks 的 License 费确实比 EMR 贵 $15K/月,看上去更贵。但算上所有成本(人力、故障损失、集群浪费),Databricks 反而便宜 18%。

这个表老板一看就懂——不用讲技术细节,数字自己说话。老板关心的是 TCO 不是技术,迁移项目要说服老板,一定要用财务语言而不是技术语言。"


面试官最爱追问的迁移问题

这 5 个追问几乎每次讲迁移项目都会被问到——一定要准备好标准答案。

追问 1:迁移需要停服吗?

标准回答框架:不需要—— 双写双读方案

"不需要停服。核心是双写双读方案——

新旧系统并行跑 4 周,所有 ETL 作业同时写 EMR 和 Databricks。下游报表 API 读什么数据源是通过配置中心控制的——迁移完成前读 EMR(主链路),迁移完成后读 Databricks。

切换是渐进式的:一张表一张表切,每张表切之前连续观察 7 天数据完全一致。业务方完全无感知。

这个方案最大的好处是——任何时候都有'保底方案'。新系统跑出问题,调度层一个开关切回旧系统,10 分钟内恢复。"

追问 2:有没有回滚过?

重点:承认回滚过比假装没有更加分

"有,一次。

第 2 批迁移中有个作业用了我们团队自己写的 UDF——这个 UDF 依赖 Hive 的一个老类,迁到 Databricks 后报 ClassNotFoundException。

回滚过程:

  1. 数据校验脚本夜里发现这个作业 Databricks 侧数据缺失
  2. 自动告警触发
  3. 我 5 分钟内看到告警,立刻在调度平台把这个作业切回 EMR 版本
  4. 下游读的是配置中心指向的 EMR 数据,根本没感知
  5. 第二天白天我重写了这个 UDF(去掉 Hive 老类依赖),测试通过后重新迁移

整个回滚+修复 8 小时,下游业务方完全无感知。

这次回滚不是项目失败,而是双写双读方案的成功验证——证明了风险控制机制有效。没有这个方案,这个问题会导致连续几天数据缺失。"

追问 3:迁移过程中谁在用旧系统的数据?

考察的是你对"主链路"和"观察链路"的理解

"迁移期间旧系统一直是主系统。下游报表、API、业务服务始终读 EMR 数据——直到某张表通过连续 7 天的数据一致性验证后,才把这张表的下游切到 Databricks。

新系统(Databricks)的定位是'观察链路'——只用于做数据对比验证,不对外提供服务。

只有在三个条件都满足时,一张表才会切换:

  1. 连续 7 天三层校验全部通过
  2. 该表的 Databricks 侧性能指标优于 EMR(或至少不差)
  3. 该表的所有下游作业在 Databricks 侧测试通过

这个顺序保证了——业务方看到的数据始终是经过完整验证的,不会有一丝风险暴露给业务。"

追问 4:这个项目你一个人做的?

这个追问是考你"个人贡献 vs 团队协作"的边界

"方案设计和风险控制是我独立负责的——整个双写双读方案、分批策略、数据校验脚本、回滚预案,都是我设计和实现的。

SQL 改写和测试是团队协作——100+ 个作业的 SQL 改写不可能一个人在 3 个月内完成。我做了两件事:

  1. 整理《方言差异对照表》——让团队里另外 2 位同事照着改 SQL,不用他们每个人都去踩坑
  2. 每批迁移的节奏把控——我决定哪些作业放哪一批、每一批什么时候做校验、什么时候切换,但具体的 SQL 改写和测试由同事并行做

我的角色是 '方案设计者 + 项目推动者 + 质量把关者',不是纯执行者。

这种表达既诚实承认团队协作(不是个人独角戏),又突出了核心价值(方案和架构都是我做的)。"

(第 9 篇会专门讲个人贡献怎么讲,更细节。)

追问 5:迁移后有什么遗留问题?

加分题——承认不足更显成熟

"两个主要遗留问题:

第一,部分老 RDD 代码没改写。 为了减少迁移改动、降低风险,我保留了一些老的 RDD API 代码(大约 5% 的作业)。这些代码在 Databricks 上能跑但性能不是最优——RDD API 不吃 Catalyst 优化器红利。迁移完成后单独花了 1 个月重构成 DataFrame API,性能又提升了 15%。

第二,监控体系没完全打通。 EMR 有一套基于 CloudWatch 的监控,Databricks 用自己的 Cluster Metrics。迁移初期这两套监控没打通,告警配置要双向维护。后来接入了统一的 Prometheus + Grafana,花了 2 周时间做告警迁移。

还有一个技术债:Delta Lake 的 Time Travel 能力我们没用起来——原本可以用 Time Travel 做数据审计和回溯,但团队习惯了'快照表'的思路,没切换过来。这是思维惯性的技术债,比代码技术债更难解决。

这些都是我清楚知道但当时没做的事——业务优先级更高,做完迁移主线先保证稳定,细节优化慢慢补。"


迁移 vs 调优 vs 数仓——面试选什么项目讲?

不同项目展示不同能力:

项目类型 展示什么能力 适合什么岗位 面试效果
迁移项目 架构能力 + 项目管理 + 风险控制 平台/架构方向 面试官印象最深
调优项目 排查能力 + 分析能力 + 量化能力 开发/调优方向 最容易出数字
数仓项目 建模能力 + 业务理解 + 规范治理 数仓方向 最常见但难差异化
实时项目 技术深度 + 架构能力 实时/流计算方向 能展示最深技术

我的选择策略

"如果面试官没指定讲什么项目,我优先讲迁移项目。原因有三:

第一,综合性最强——迁移项目涉及技术选型、方案设计、风险控制、数据校验、团队协作——5 个维度一起展示。其他项目类型只能展示 2-3 个维度。

第二,结果最有冲击力——'零故障完成 100+ 作业迁移'这句话本身就有震撼力。面试官听到'零故障'会立刻想深入追问。

第三,稀缺性最高——90% 的候选人没做过迁移项目,或者做过但没做到零故障。这是差异化的最佳武器。

如果面试官想听更多技术深度,我会补充调优项目的故事('我还做过一个 Spark SQL 调优,6 小时优化到 40 分钟');想听数仓业务,我会补充数仓项目('我还在某短剧出海公司从 0 搭建了广告数据数仓')。

迁移作为主菜,调优和数仓作为配菜——这是我面试讲项目的标准组合拳。"


其他高频追问

追问 6:如果 Databricks 比 EMR 贵怎么说服老板?

前面有 TCO 对比表,这里展开讲怎么说服

"说服老板的核心是——用财务语言不用技术语言。

老板不关心 AQE、Z-Ordering 这些词,老板关心TCO(总拥有成本)。

我做了一张 TCO 对比表:

成本项 EMR Databricks
云账单 $60K/月 $55K/月
运维人力 $5K $1K
故障损失 $3K 0
总计 $68K $56K
年节省 $144K

这张表让老板 30 秒就能看懂:

  • Databricks License 虽然贵 $15K/月,但省了其他成本
  • 运维人力从 0.5 FTE 降到 0.1 FTE(NameNode 抖动归零、YARN 调优下线)
  • 故障损失归零(过去每月几次 ETL 超时影响业务晨会)

第二个关键——量化未来价值。 除了成本,还讲 Databricks 的新能力能解锁什么:

  • Delta Lake 的 ACID 能让数据治理更规范(减少数据质量事故成本)
  • Z-Ordering 能让查询快 70%(提升分析师工作效率)
  • Unity Catalog 能做数据血缘和权限管理(合规要求)

老板听完这些,不用我再推销,他自己会拍板。"

追问 7:迁移用了 3 个月会不会太长?

反向论证——快反而危险

"3 个月其实已经很快了——平均每个作业 1 天不到。

详细时间拆解:

  • 2 周:评估和方案设计(梳理作业依赖、设计双写双读机制、写数据校验脚本)
  • 4 周:第 1-2 批迁移(30+40=70 个作业,并行处理)
  • 4 周:第 3-4 批迁移(25+10=35 个作业,更谨慎)
  • 2 周:灰度切换和最终验证
  • 2 周:收尾和文档沉淀

为什么不能更快?因为风险不可控。

如果'一刀切'1 周迁完——技术上可行,但一旦数据不一致,100+ 作业一起回滚,对业务影响巨大。

迁移项目的核心 KPI 是'零故障',不是'速度'。

实际上我们业内有个说法:'快速迁移是赌博,慢速灰度是工程'。

3 个月双写双读是业界标准做法——Netflix 类似规模的迁移花了 6 个月、Airbnb 花了 4 个月。3 个月已经是比较快的。"

追问 8:如果现在让你选,还会选 Databricks 吗?

反思题——考察你的技术判断力

"还是会选 Databricks,但理由会有调整。

迁移 2 年后回看这个决策:

当时选 Databricks 的原因——Delta Lake 生态、性能提升、运维简化。这些事后验证都是对的。

现在回看新增的考虑——

1. 流批一体方向。如果今天做选型,我会更重视流批一体能力——用 Paimon 或 Iceberg 加上 Flink 构建'一套代码同时支撑实时和离线'的架构。Databricks 虽然也支持但更偏向批处理生态。

2. Vendor Lock-in 风险。2 年过去,对 Databricks 生态的依赖越来越深(Delta Lake、Unity Catalog、Notebook)。一旦 Databricks 涨价或者服务变化,迁移成本巨大。这是当时没充分评估的风险。

3. 开源方案成熟度。2 年前开源 Delta Lake + Spark on K8s 还不够成熟,现在这套开源方案已经能支撑大部分场景,成本更低、更灵活。如果今天做决策,我会认真评估这个方向。

所以答案是——会选,但会更谨慎地评估流批一体和 Vendor Lock-in。

一个技术决策是否正确,要放在当时的上下文里看,不能用今天的标准判断 2 年前的决策。当时 Databricks 是最优解,现在可能不是,这很正常。"


面试回答分层——60 vs 75 vs 90 分

60 分回答(参与者视角)

"我参与过一个 EMR 到 Databricks 的迁移项目。我们团队迁了 100 多个作业,用了几个月,最后性能提升了,成本也降了。"

面试官内心 OS:"这人大概率是跟着做的,连自己负责什么都说不清楚。"

75 分回答(有结构但没深度)

"我在某公司主导了 EMR 到 Databricks 的迁移。用了双写双读方案,分了几批迁移。100+ 作业迁移完,ETL 性能提升了 40%。过程中没出大故障。"

面试官内心 OS:"结构有了,结果也有,但方案细节、风险控制、校验机制全没讲。像是读了某本项目管理书。"

90 分回答(前面完整示范那版)

四个关键点全齐:

  • 为什么迁:EMR 三大痛点(运维/性能/成本),数据证明
  • 怎么迁:双写双读 + 分批策略(4 批、按风险排序)+ SQL 方言改写 + 完整校验
  • 风险控制:三层校验、回滚机制、真实回滚故事
  • 效果:ETL -40%、查询 -70%、存储 -35%、运维 -60%、报表提前 4 小时产出、方案被其他团队复用

面试官内心 OS:"这人真做过迁移,每个细节都有思考,可以谈更深的架构话题了。"


总结

迁移项目是大数据面试里最能体现工程成熟度的项目类型。要讲出 90 分,核心是——

  • 四个关键点全齐:为什么迁(痛点数据化)、怎么迁(方案设计)、风险怎么控制(校验+回滚)、效果怎么量化(三维对比)
  • 强调"零故障"不是口号:业务可感知的零故障 = 方案设计 + 分批策略 + 数据校验 + 回滚预案 四位一体
  • 分批策略讲"思考"不讲"动作":不是我分了 4 批,是按风险从低到高排序,用低风险积累经验、用高风险验证成果
  • SQL 方言差异讲具体代码对比:日期函数、INSERT 语法、LATERAL VIEW——两段代码对比是"真改过"的硬证据
  • 数据校验必须是三层:行数匹配、聚合值误差、抽样明细——每一层都有独特检测能力
  • 回滚故事敢于承认:真实发生过的回滚比假装没有更加分
  • 说服老板要用 TCO 表:财务语言不是技术语言,LicenseSum 贵但 TCO 降 18% 才是决策点
  • 面试策略:迁移项目是稀缺技能——做过的人不多,做到零故障的更少。这是差异化的最佳武器,一定要放在简历最显眼的位置,面试时优先讲

下一篇

刷题进度保存在当前浏览器