4. 数据湖仓迁移策略

把传统数据湖或数仓迁移到 Delta Lake,本质上不是“把文件换个目录”,而是把数据组织方式从“文件集合”升级为“带事务日志的表”。传统 Hive 表或 Parquet 目录只记录文件位置,读时靠目录扫描推断分区;Delta Lake 在表目录下维护 _delta_log/,用 JSON 提交记录每次写入的 add/remove 文件、schema、统计信息和事务版本。因此迁移策略必须同时回答三件事:分层怎么落、增量怎么同步、出错怎么回滚。

一、Bronze/Silver/Gold 分层的迁移映射

迁移时不要按原系统表名平铺到新环境,而应先按“数据加工深度”重新归位。Bronze 层承接原始接入,保留源系统字段、原始类型和接入时间,允许存在重复与脏数据;Silver 层做清洗、去重、类型规范、维度补全和一致性校验,是可信明细层;Gold 层面向主题域和消费场景,做聚合、宽表或指标计算。传统数仓的 ODS 通常映射到 Bronze,DWD/DWB 映射到 Silver,DWS/ADS 映射到 Gold;传统数据湖的原始日志区映射 Bronze,清洗后 Parquet 映射 Silver,报表集市映射 Gold。

分层落位后,每层表的 Delta 属性要区别设置。Bronze 表通常启用 delta.appendOnly=true,避免误更新原始数据;Silver 表启用 schema 演进与 mergeSchema,并建立主键去重逻辑;Gold 表按查询模式选择分区列和 Z-Order 列。下面这张图给出迁移时的分层映射与数据流关系,重点看“源系统 → 分层落位 → Delta 表属性”的对应关系。

传统湖/仓 → Delta Lake 分层迁移映射 源系统 业务库 CDC / 日志 数仓 ODS / DWD 报表集市 / 文件 接入 Bronze 原始层 保留源字段与原始类型 appendOnly = true 按接入日期分区 允许重复与脏数据 _ingest_time 审计列 清洗 Silver 可信层 去重 / 类型规范 MERGE 主键更新 schema 演进开启 维度补全与校验 按业务日期分区 聚合 Gold 主题宽表 指标聚合 Z-Order OPTIMIZE 面向 BI 消费 增量同步机制 CDC / 增量抽取 按 LSN / 时间戳水位 MERGE INTO 按主键 upsert 水位表:记录 last_sync_ts / last_version 每次同步前读取,成功后更新 幂等保证:同一批次重复执行结果一致 依赖主键 + 版本号去重 回滚方案 时间旅行回滚 RESTORE TABLE ... VERSION 版本审计 DESCRIBE HISTORY 回滚前校验:行数、主键唯一性、关键指标对比 确认无误后再切换下游读取 保留策略:VACUUM 前保留 7 天历史版本 确保回滚窗口足够覆盖故障发现周期

二、增量同步:水位、MERGE 与幂等

全量搬迁只适合首次初始化,日常必须走增量。增量同步的核心是“水位 + 主键 + 幂等”。水位记录上次同步到的位置,可以是时间戳、数据库 LSN 或 Kafka offset;主键决定 MERGE 的匹配条件;幂等保证同一批数据重复执行不会产生重复行。

以订单表为例,源库有 order_id、updated_at、status 等字段。首次全量导入 Bronze 后,后续每次从水位表读取 last_sync_ts,抽取 updated_at > last_sync_ts 的记录写入 Bronze 临时区,再 MERGE 到 Silver:

MERGE INTO silver.orders AS t
USING bronze.orders_inc AS s
ON t.order_id = s.order_id
WHEN MATCHED AND s.updated_at > t.updated_at THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;

-- 成功后更新水位
UPDATE ctl.sync_watermark
SET last_sync_ts = (SELECT MAX(updated_at) FROM bronze.orders_inc),
    last_version = (SELECT MAX(version) FROM (DESCRIBE HISTORY silver.orders))
WHERE table_name = 'silver.orders';

这里有两个容易踩的坑。第一,水位更新必须放在 MERGE 成功之后,否则中途失败会导致数据丢失;第二,MERGE 的匹配条件要包含 updated_at 比较,避免旧版本覆盖新版本。如果源系统存在物理删除,还需要额外用 WHEN NOT MATCHED BY SOURCE THEN DELETE 或软删除标记处理,不能只靠 upsert。

三、回滚方案:时间旅行 + 校验 + 保留窗口

Delta Lake 的 _delta_log 天然支持时间旅行,这是迁移过程中最重要的安全网。每次写入都会生成新版本,回滚只需 RESTORE TABLE silver.orders TO VERSION AS OF 12 或 TO TIMESTAMP AS OF '2025-01-15 10:00:00'。但回滚不是无条件的:必须先确认目标版本的数据质量,再执行恢复,最后验证下游读取是否正常。

建议的回滚判断依据包括三项:目标版本行数与预期偏差是否在阈值内、主键是否唯一、关键指标(如订单总金额)与回滚前是否一致。若偏差超过阈值,说明目标版本本身也有问题,应继续向前回滚或从 Bronze 重放。同时要注意 VACUUM 会物理删除旧文件,默认保留 7 天,迁移期间建议把保留窗口设为 14 天,确保故障发现周期内都能回滚。

迁移期间不要对 Bronze 表执行 VACUUM,也不要关闭 Delta 日志。Bronze 是重放源头,一旦旧文件被清理,回滚和重算都会失去依据。

四、迁移执行顺序与验收

推荐按“先 Bronze、再 Silver、后 Gold”的顺序迁移,每层完成后再进入下一层。Bronze 迁移重点是接入完整性和分区对齐;Silver 迁移重点是主键去重和 schema 兼容;Gold 迁移重点是聚合口径与查询性能。每层迁移后都要做一次对账:源表与目标表的行数、主键数、关键字段空值率、金额汇总值四项对比,偏差超过 0.1% 就要暂停并排查。

验收通过后,再切换下游任务读取新表,并保留旧表一个周期作为兜底。整个迁移过程中,水位表、版本历史和回滚脚本要作为标准交付物一并维护,确保任何一次同步失败都能在分钟级定位并恢复到已知良好版本。