ingestr 的 --incremental-strategy:默认值、边界来源与静默跳过
站内已有一篇对 ingestr 的泛介绍,这篇只讨论这一条配置项:--incremental-strategy 的默认值是什么、delete+insert 依赖哪两个边界、边界缺失时会发生什么,以及如何确认策略真的生效了。安装和源端选型不在此展开。
- 项目:
- 文档:
- PyPI:
默认是 replace,不传策略不等于增量
--incremental-strategy 的候选值为 replace、append、delete+insert、merge、scd2,默认 replace。
也就是说,省略该参数时执行的是一次替换:ingestr 按目标库选择对应的替换实现。单表 PostgreSQL 加载时,replace 保留表对象,不会把表重建。注意不是所有源 × 目标组合都支持全部策略,不支持的组合在运行时直接失败,而不是降级。
replace 与增量无关。判断某次运行是否增量,要看你是否显式指定了 append、delete+insert、merge 或 scd2,以及配套的 key / 边界参数是否给全。
另一个历史项:旧的 truncate+insert 策略已被移除。留着这个值的旧脚本需要改成 replace,改完之后目标端不会按旧 truncate+insert 的方式执行,而是走 replace 的目标库专属实现。
delete+insert 需要上下两个边界
delete+insert 的逻辑是:从源端读取区间内的数据,在目标端删掉该区间的旧行,再插入新行。它需要一对边界:
--interval-start:区间含端点的下界,同时作为删除的下边界,并在源端支持区间过滤时下推给源端读取。--interval-end:区间含端点的上界,同时作为删除的上边界,同样可下推。
两者默认都是 None。省略时,ingestr 会尝试从 staged rows 推断缺失的边界。如果推断不出来,delete+insert 会跳过 delete 和 insert,并且不报错。
失效点就在这一句:命令返回成功,日志里可能什么都没有,目标表没变化。可判定的情形只有下面几种:
| 显式 start | 显式 end | staged rows 能否推断缺失边界 | 实际行为 |
|---|---|---|---|
| 给了 | 给了 | 不涉及 | 用给定值作删除下界 / 上界,正常执行 |
| 给了 | 缺 | 能 | 上界从 staged rows 推断,正常执行 |
| 缺 | 给了 | 能 | 下界从 staged rows 推断,正常执行 |
| 缺 | 缺 | 能 | 两侧都从 staged rows 推断(数值 key 的情形) |
| 任意一个缺失 | — | 不能 | 跳过 delete 和 insert,不报错 |
排查“增量没生效”时,先确认上表落在哪一行,再去看数据。显式给全 --interval-start 和 --interval-end 是最稳的做法。
--incremental-key 该用哪类列
delete+insert 下,--incremental-key 是决定替换目标哪些行的区间列。正常应该用日期、时间戳、分区列或数字批次列,而不是行主键:行主键在目标端不构成连续区间,删不干净也删不对。
其它策略下它的语义不同:append 和 merge 把这个列传给源端读取做区间过滤(源端支持时),其中 append 不会拿它和目标表比较,只做源端过滤。默认值为 None。
一个容易踩的细节:--interval-start 和 --interval-end 按 datetime 解析,不接受数值。所以数值型的 delete+insert key 只有在边界由 staged rows 推断时才成立;如果你显式传数值型边界,参数解析这一步就不对。
merge 的 --primary-key 与目标库已有约束
--primary-key 用于 merge 和 scd2 标识一行逻辑记录,组合键就多次传该参数。delete+insert 下部分目标库可以用它去重 staged rows,但这属于目标库特定行为,不能当作通用保证。
主键值应为非 NULL。原文明确了两类不一致:有些目标库在 merge 时把 NULL 键当作相等来匹配,另一些直接拒绝或产生重复行。同一份数据换个目标库,结果可能从“重复”变成“报错”。
还有一条常被忽略:这是 ingestr 自身的策略配置,不要只依赖目标库里已经存在的主键约束。约束由目标库执行,策略匹配由 ingestr 执行,两者不是一回事。
--incremental-predicate 与 merge 的匹配条件相关,但用错代价很高:它会被追加到目标端匹配条件上,要求所有匹配到的目标行都满足该谓词,否则会插入重复或撞唯一约束。BigQuery 和 Trino 用 t 作目标别名,其它支持的 SQL 目标用 target。
最小可复现命令
MySQL → Postgres,显式给全边界。以下命令按官方文档参数拼装,未在本机实测,URI 中的账号、库名、表名按实际环境替换:
ingestr ingest \
--source-uri "mysql://reader:secret@127.0.0.1:3306/shop" \
--source-table "shop.orders" \
--dest-uri "postgresql://postgres:secret@127.0.0.1:5432/analytics" \
--dest-table "public.orders" \
--incremental-strategy "delete+insert" \
--incremental-key updated_at \
--interval-start "2024-01-01T00:00:00" \
--interval-end "2024-02-01T00:00:00" \
--primary-key id \
--debug
--source-uri 与 --dest-uri 必填;--dest-table 省略时默认取 --source-table;--source-table 除 CDC 源(多表)外也是必填。
怎么确认策略真的生效了
--debug:部分目标库会在 debug 日志里打印生成的 SQL。直接看那条 DELETE 的 WHERE 上有没有区间条件,比猜快。--progress json:以 JSON 形式看进度输出。- 目标表的
_ingestr_loaded_at/_ingestr_run_id:用它们区分某批行是哪次运行写入的,能判断这次运行到底插没插。 --stream场景下的--metrics-addr:以 Prometheus 格式在/metrics暴露复制延迟与吞吐指标。
--stream 由 CDC 源(postgres+cdc、mysql+cdc、mariadb+cdc、vitess+cdc、ps_mysql+cdc、mssql+cdc、mongodb+cdc)和消息中间件支持。刷写由 --flush-interval(默认 30s)或 --flush-records(默认 50000)任一先到触发,--flush-records 同时也是内存上界。投递语义是 at-least-once,配合按主键 / msg_id 的 merge 让重放幂等。
还悬着的问题
边界推断的具体失败条件,目前只能通过“参数给没给全 + 结果表变没变”来反推,没法在运行前静态判定某次 delete+insert 会被跳过。
数值型 delete+insert key 从 staged rows 推断出的边界,与显式传入等值区间,在语义上是否完全等价,也没有直接对照。
merge 路径下各目标库对 NULL 主键的处理差异(视为相等 / 拒绝 / 重复),会不会在 replace 或 delete+insert 路径上以另一种形式再次出现,取决于目标库实现细节。