首页/数据/数据工程与 ETL 速查/增量抽取水位列与半开区间
管道基础52 条

增量抽取水位列与半开区间

用更新时间列做水位是最常见的增量方式:记录上一次抽取到的高水位值,下次只取大于它的数据。用「大于等于左边界、小于右边界」的半开区间避免边界重复与丢失。

水位列与半开区间

用更新时间列做增量是最常见的抽取方式:记录上一次抽取到的最大值(水位),下次只取之后的数据。关键在于区间的写法——使用「大于等于左边界、小于右边界」的半开区间,并把水位推进到本次窗口的结束时间,而不是推进到本次取到的最大时间戳。

为什么边界最容易丢数

同一时间戳可能对应多条记录:如果用「大于上次最大值」来取,同一秒内的其余记录会被永久跳过。反之如果把水位推进到「本次数据的最大时间戳」,那些时间戳相同但尚未落库的记录也会被跳过。用窗口结束时间推进水位可以同时避开这两种情况。

基本信息

常用水位列updated_at、gmt_modified、自增 ID
水位保存控制表、调度变量或参数文件,不能存在脚本本地
区间写法[last_watermark, window_end),左闭右开
推进方式任务成功提交后把水位更新为窗口结束时间
缺失能力无法感知物理删除与时间戳回拨

常用水位列对比

水位列优点局限
更新时间列贴近业务修改时间,易理解依赖业务维护字段,补录旧时间的数据会漏采
自增主键单调递增,边界清晰只反映新增,无法捕获更新
数据库日志位点能捕获增删改,边界精确需要日志权限与配套工具

典型使用场景

  • 订单、流水等大表的按日增量抽取
  • 外部 API 按游标分页拉取数据,游标即水位
  • 多任务串联时的断点续跑:记录水位后失败可从此继续
  • 补数:把水位回拨到目标日期前一秒,重跑指定区间
  • 对账:用水位区间与目标端条数比对,确认没有漏采

增量 SQL 模板

增量 SQL 模板
-- 左闭右开:不会漏掉边界时间戳上的其它记录,也不会重复上一次窗口末尾
SELECT id, amount, updated_at
FROM source_order
WHERE updated_at >= :last_watermark
  AND updated_at <  :window_end
ORDER BY updated_at;

水位更新(控制表)

水位更新(控制表)
-- 数据成功写入目标后才推进水位,顺序不能颠倒
UPDATE etl_watermark
SET last_value = :window_end,          -- 推进到窗口结束时间,而不是数据里的最大时间戳
    updated_at = NOW()
WHERE job_name = 'order_incr';
水位推进与数据写入必须放到同一个成功边界内:先写数据、成功后再更新水位。反过来写的话,任务在两者之间失败会永久跳过这一批数据。
源端时间戳回拨或补录旧时间的数据会漏采:这类记录的时间早于当前水位,永远不会被增量查询覆盖,只能靠定期对账与全量兜底补回来。

易错点

  • 用「大于上次最大值」而不是左闭右开区间,同一时间戳上的其余记录被永久跳过
  • 先推进水位再写数据,任务中途失败会造成一段区间永久缺失
  • 水位保存在脚本本地文件,换机器或容器重建后丢失,从最早或当前位置重新开始
  • 不设全量兜底,遇到源端时间戳回拨或补录历史数据时无法察觉漏采

数据来源与参考

  • 行业通用增量抽取模式(水印法)
  • 《Data Warehouse ETL Toolkit》增量加载实践
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌