我们如何在不使用逻辑复制的情况下从 Heroku 迁移 TB 级数据

我们如何在不使用逻辑复制的情况下从 Heroku 迁移 TB 级数据

💡 原文英文,约2600词,阅读约需10分钟。
📝

内容提要

PlanetScale 推出开源工具 Heroku Migrator,帮助 Heroku 标准版用户在线迁移海量数据。该工具先用 ps-discovery 和 Liftoff 评估兼容性与风险,再基于 Bucardo 触发器复制,先拷贝数据、后并行重建索引,并加入校验与可控切换。v2.0 解决了拷贝中断重来、长连接静默断开和 delta_check 卡死三大问题,使迁移更快更稳。

🔎

延伸解读

触发器复制的适用边界

Heroku 标准版不提供逻辑复制,因此基于触发器的复制成为唯一可行的在线迁移方案。但触发器复制要求每张表必须有主键或唯一索引,否则无法定位要更新的行。此外,迁移期间必须冻结所有 DDL 操作,包括框架的自动迁移和 pg_partman 维护任务,否则会破坏迁移。这些限制意味着迁移前必须通过 ps-discovery 和 Liftoff 充分评估,提前补齐主键、处理不兼容扩展,并规划好停机窗口。

分阶段拷贝与索引重建的收益

v2.0 将数据拷贝拆分为先传数据、后并行重建索引两个阶段。拷贝前删除目标库的二级索引和唯一索引,仅保留主键,使拷贝速度从约 60 GB/h 提升到近 300 GB/h;拷贝完成后再用并行 worker 重建索引,耗时约 2 小时。整体迁移速度提升约 2.5 倍。更重要的是,坏索引导致的问题从拷贝中途失败变为重建阶段可重试,避免了数小时拷贝从头开始的风险。

长连接与 delta_check 的隐藏陷阱

大库迁移中,长时间保持的连接可能被网络路径静默断开,而 libpq 默认的 TCP keepalive 探测间隔长达两小时,导致 worker 挂起而不报错。v2.0 通过设置 keepalives 参数让断连在一分钟内暴露。另一个问题是 Bucardo 的 delta_check 函数在 track 表上缺少 txntime 索引,大库下会全表扫描导致卡死。迁移工具在初始化时为空表创建该索引,几乎无锁且瞬间完成,避免了迁移后期数小时的停滞。

❓

Q&A

Heroku 标准版为什么不能像其他 Postgres 迁移那样用逻辑复制?

Heroku 标准版不提供 WAL 访问和逻辑复制功能,因此无法使用基于复制槽的迁移方式。逻辑复制仅在 Heroku Postgres Advanced 计划中提供(自 2026 年 10 月起),但标准版用户无法直接使用,除非先迁移到 Advanced 计划,这又需要额外迁移。

PlanetScale 的 Heroku Migrator 工具是如何在不使用逻辑复制的情况下同步数据的?

Heroku Migrator 基于开源的 Bucardo 触发器复制工具。它在源数据库的每个表上安装 bucardo_delta 触发器,记录所有插入、更新和删除操作到 delta 和 track 表。然后通过一次性的 COPY 操作将现有数据加载到 PlanetScale,之后 Bucardo 工作进程读取并应用记录的变更,保持目标数据库与源数据库同步。

迁移前需要做哪些准备工作?ps-discovery 和 Liftoff 分别起什么作用?

迁移前需运行 ps-discovery 工具收集源数据库信息(版本、配置、扩展、模式、表、索引等),并获取 Heroku Postgres 附加组件的详情。然后将结果上传到 Liftoff 工具,它会分析扩展兼容性、模式缺陷和阻塞项,推荐迁移路径并估算时间,还会标记缺少主键或唯一索引的表(触发器复制无法迁移这些表)。

Heroku Migrator v2.0 如何解决数据拷贝中断后需要从头开始的问题?

v2.0 将数据拷贝阶段拆分为两步:先传输数据,再重建索引。在拷贝前,迁移工具会删除目标数据库上的所有二级索引和唯一索引(保留主键),并将索引定义保存到表中。拷贝完成后,并行重建索引。这样即使拷贝过程中出现错误(如索引大小超限),也只需在重建步骤中重试,而无需重新拷贝所有数据。同时,设置 transaction_timeout=0 避免长事务被中断。

迁移过程中遇到网络静默断开导致复制停止,v2.0 是如何修复的?

v2.0 为 Bucardo 的连接设置了 TCP keepalive 参数(tcp_keepalives_idle=60, tcp_keepalives_interval=10, tcp_keepalives_count=6),使断开的连接能在约一分钟内报错而不是无限等待。此外,对于超长 COPY 操作,还调整了 EC2 主机的 TCP 缓冲区大小(net.core.rmem_max 等参数)以改善网络性能。

delta_check 卡死是什么原因?如何解决?

delta_check 卡死是因为 Bucardo 的 bucardo_delta_check() 函数在 delta 表和 track 表之间进行反连接查询时,仅使用 txntime 条件,而 track 表的索引是 (target, txntime),无法利用该索引,导致在大表上全表扫描。解决方案是在每个 track 表的 txntime 列上创建 B-tree 索引,迁移工具在设置阶段(表为空时)自动创建,避免锁表。

使用 Heroku Migrator 进行迁移的典型步骤有哪些?

典型步骤包括:1) 运行发现工具并在 Liftoff 中审查结果;2) 部署迁移工具(Heroku dyno 或 EC2 实例);3) 启动迁移,复制模式并设置复制;4) 开始数据拷贝,等待完成并自动重建索引;5) 切换流量(可先开启维护模式);6) 验证迁移;7) 将应用指向 PlanetScale;8) 完成迁移并删除迁移工具。

🏷️

标签

➡️

继续阅读