4. 增量备份策略:从原理到实战

增量备份,说白了就是只备份「从上次备份以来发生变化的那部分数据」。全量备份像搬家——把所有东西打包;增量备份则像每天记日记——只记录今天新增或改动的部分。在 Lakehouse 架构下,这个思路能帮我们省下大量存储和计算资源。

我刚开始做数据备份时,也犯过「每天跑全量」的傻事。那时候数据量才几百 GB,跑一次全量也就半小时。后来数据涨到几十 TB,全量备份要跑 6 个多小时,还经常把生产集群搞慢。嗯,增量备份就是用来解决这个问题的。

4.1 增量备份的核心原理

增量备份的底层逻辑其实很简单:记录变化。每次备份只处理那些「新产生」或「被修改」的数据文件,而不是把整个表重新拷贝一遍。

核心公式:

增量备份数据量 ≈ 上次备份以来的数据变更量

全量备份数据量 ≈ 整个表/库的总数据量

当数据变更率低于 20% 时,增量备份的效率优势非常明显。

在 Lakehouse 架构中,增量备份通常依赖两个关键能力:

  • 文件级别的变更追踪——知道哪些 Parquet/ORC 文件被新增、修改或删除
  • 事务日志的版本管理——通过事务日志(如 Delta Lake 的 _delta_log)定位变更范围

我遇到过的一个典型场景:某电商平台每天新增约 500GB 订单数据,但全量数据有 50TB。如果每天跑全量备份,存储和计算成本都扛不住。改用增量备份后,每天只处理那 500GB 的新增数据,备份时间从 6 小时降到了 20 分钟。

4.2 基于时间戳的增量捕获

这是最直观的增量捕获方式。你想想看,每条数据都有一个「最后修改时间」或「创建时间」,我们只需要查询 WHERE update_time > last_backup_time 就能拿到增量数据。

具体实现上,我一般会这样设计:

-- 假设上次备份时间戳为 '2024-01-15 03:00:00'
-- 增量捕获 SQL 示例
INSERT INTO backup_table
SELECT * FROM source_table
WHERE update_time > '2024-01-15 03:00:00'
  AND update_time <= '2024-01-16 03:00:00';

我的经验:时间戳字段一定要建索引,否则全表扫描会拖垮性能。另外,时间戳的精度要统一——我见过有人用秒级时间戳,有人用毫秒级,结果漏掉了同一秒内的多条变更。

时间戳方案有几个明显的坑:

  • 时钟不同步问题——分布式环境下,各节点的时间可能不一致,导致数据漏采或重复
  • 删除操作无法捕获——如果数据被物理删除,时间戳字段也跟着消失了
  • 历史数据修改——修改了旧数据的时间戳,增量查询可能会漏掉

我曾经在一个金融项目中踩过这个坑。业务方修改了三天前的交易记录,但时间戳没更新,增量备份完全没感知到。后来我们不得不加了一个「变更日志表」来兜底。

4.3 基于 Change Data Feed 的增量备份

Change Data Feed(CDF)是 Lakehouse 平台提供的一种更可靠的增量捕获机制。以 Delta Lake 为例,CDF 会记录每一行数据的插入、更新和删除操作,并生成一个可查询的变更事件流。

它的工作原理是这样的:

  1. 每次写入操作(INSERT/UPDATE/DELETE/MERGE)都会在事务日志中记录变更
  2. CDF 将这些变更整理成结构化的行级变更记录
  3. 用户可以通过版本号或时间戳范围查询变更数据

启用 CDF 的配置很简单:

-- 在创建表时启用 Change Data Feed
CREATE TABLE sales_data (
  order_id STRING,
  amount DOUBLE,
  order_date DATE
) USING DELTA
TBLPROPERTIES (
  'delta.enableChangeDataFeed' = 'true'
);

-- 查询某个版本范围内的变更
SELECT * FROM table_changes('sales_data', 10, 15);

-- 或者按时间戳查询
SELECT * FROM table_changes('sales_data', '2024-01-15 03:00:00', '2024-01-16 03:00:00');

注意:CDF 会额外占用存储空间来保存变更记录。我建议根据业务需求设置合理的保留期,比如只保留最近 7 天的变更数据。保留太久会浪费存储,保留太短又可能影响恢复点目标(RPO)。

相比时间戳方案,CDF 有几个明显的优势:

对比维度 时间戳方案 Change Data Feed
删除操作捕获 ❌ 无法捕获 ✅ 完整记录
数据一致性 ⚠️ 依赖时钟同步 ✅ 事务级一致
历史修改追溯 ⚠️ 可能遗漏 ✅ 完整变更历史
存储开销 ✅ 几乎无额外开销 ⚠️ 需要额外存储
查询性能 ⚠️ 全表扫描风险 ✅ 索引化变更记录

我个人更推荐在生产环境中使用 CDF 方案。虽然多了一点存储开销,但换来的是数据一致性和可追溯性,这笔账算下来是划算的。

4.4 增量备份的合并策略

增量备份做多了,就会产生大量的小文件。你想想看,每天一个增量文件,一个月就是 30 个文件。查询时扫描这么多小文件,性能会急剧下降。

合并策略就是为了解决这个问题。常见的合并方式有三种:

  • 定期全量合并——每周或每月做一次全量备份,然后清理旧的增量文件
  • 增量文件合并——将多个小增量文件合并成较大的文件,减少文件数量
  • 分层合并——按时间粒度分层,比如每小时增量 → 每日增量 → 每周全量

我常用的合并策略是这样的:

-- Delta Lake 的 OPTIMIZE 命令可以合并小文件
OPTIMIZE backup_table
WHERE backup_date >= '2024-01-01'
  AND backup_date < '2024-02-01';

-- 同时清理过期的变更数据
ALTER TABLE backup_table
SET TBLPROPERTIES (
  'delta.logRetentionDuration' = 'interval 7 days',
  'delta.deletedFileRetentionDuration' = 'interval 7 days'
);

我的建议:合并操作最好放在业务低峰期执行。另外,合并频率要根据数据变更率来定——变更频繁的表可以每天合并一次,变更少的表一周一次就够了。

下面这张图展示了增量备份的完整流程和合并策略:

增量备份流程与合并策略 数据源表 增量捕获方式 时间戳捕获 Change Data Feed 增量备份文件 合并策略 定期全量合并 每周/月做一次全量 增量文件合并 OPTIMIZE 合并小文件 分层合并 小时→天→周分层 高效可恢复的增量备份集 💡 合并策略选择 数据变更率 > 20%:每天合并 数据变更率 < 5%:每周合并

在实际项目中,我一般会这样设计合并策略:

  • 高频增量(每小时)——每 6 小时做一次小文件合并,控制文件数量
  • 日增量——每天凌晨做一次 OPTIMIZE,合并当天产生的增量文件
  • 周全量——每周日做一次全量备份,同时清理超过 7 天的增量文件

避坑提醒:合并操作本身也会产生新的文件版本。如果合并频率太高,反而会增加版本管理的复杂度。我曾经在一个项目中每小时合并一次,结果版本数暴增,查询时版本解析成了瓶颈。后来调整为每天合并一次,问题就解决了。

增量备份的合并策略没有银弹。你需要根据数据量、变更频率、恢复时间目标(RTO)和恢复点目标(RPO)来综合权衡。我的经验是:先跑一段时间看看数据增长趋势,再决定合并频率,不要一开始就定死。