一次切干净还是渐进式:数据库割接的阶段、回滚,与 PgDog 的 CUTOVER 机制
割接不是切一条 DNS 记录。本文按 AWS 迁移割接阶段与 ttbz 数据库迁移规程,梳理摄取冻结、最终同步、路由切换与回滚路径,再拆解 PgDog 的
CUTOVER命令、阈值配置、逆向复制,以及反向复制不停导致的 WAL 堆积坑。
cut-over 到底指哪个动作
按 ttbz 数据库迁移规程的定义:应用从「操作源数据库、数据同步至目标库」的一致性状态,切换到「操作目标数据库、数据反向同步回源库」的一致性状态,这个动作就是割接。
关键在最后一句:正逆向复制的分界点就是生产割接。割接前是正向复制(源 → 目标),割接后是逆向复制(目标 → 源),后者可选但决定了回滚成本。迁移整体分三阶段——迁移准备(评估/方案/物料/可行性验证)、迁移实施(对象迁移/功能适配/数据迁移/数据比对/生产验证)、并线运行(并线准备/并线实施/生产割接/保障优化);三个阶段里应用一直连源库,直到生产割接才切到目标库。
割接阶段
AWS Prescriptive Guidance 的 cutover stage把割接拆成五步:
- Ingestion freeze(摄取冻结):冻结本地应用和数据进入数据库,保证割接期间没有新事务或新数据。
- Backup(备份):对本地系统做最终备份,必要时用于紧急回滚。
- Data sync(数据同步):完成本地与目标之间的最终数据同步。
- Routing changes(路由变更):把用户路由到新环境,例如更新 DNS 记录或改负载均衡器目标。
- Testing(测试):标记迁移完成前验证一切正常。
如果数据一致性是关键要求,需要在割接开始前锁定源环境(比如数据库锁)。锁能保证源端没有新事务,代价是更长的停机窗口。
all-at-once 与 phased
一次性(all-at-once):一个开关切换整个方案——更新 DNS 或改负载均衡器,所有用户和实时流量立即走新系统。适用于因主机名冲突、许可证问题或域认证约束无法并行上线的场景。这种方式时间关键,重点是谁、在什么时机发起 failback,建议配大量性能测试与回归测试。
渐进式(phased / canary):在规定时间段内逐步割接,持续监控当前系统能否承受负载、每个组件是否按预期运行,可按反馈调整,出问题更容易回滚。做法是把构成应用的一定比例服务器和服务拆出来切到 AWS,其余留本地,再按负载均衡或 DNS 策略把客户端流量路由到两个环境。
选哪种,看几件事:同一 move group 内有哪些依赖应用/服务、哪些数据源可共用、哪些应用与基础设施能把部分负载重定向到不同端点。如果应用无法容忍数据源与应用服务器之间延迟增加,这就是需要一次性割接的明确信号,此时应用资源(服务器和数据库)要一起切。渐进式的成立条件更依赖底层应用:是否有可拆分的多层(前端、后端、数据库)弹性服务器对或组?是否通过负载均衡器访问、且该负载均衡器支持同时路由到 AWS 网络与本地网络?迁到 AWS 的应用服务器能否容忍到数据库等后端依赖的延迟?
经验做法:开发和测试环境能容忍停机,用一次性割接,简单、工作量低;业务关键的生产负载用渐进式,更复杂耗时,但停机更低、回滚更快。
时机与割接前测试
一般选用户最少的时间窗口,业务影响最小,同时要和支持团队可用性平衡,并把日期时间与干系人就绪度一起考虑。
割接前做功能与非功能测试,比如负载测试。此时 AWS 环境不服务实时流量,测试是非破坏性的。有些场景因为依赖其他应用,无法在生产前全部测到,需要记录潜在风险、如何识别、以及测试失败时的缓解方案。割接前建议做一次 operational readiness review,评估测试完整性、团队监控与告警能力、干系人是否清楚如何支持维护。
回滚的两条路
还没产生新数据时:恢复本地实例,然后修改负载均衡器或 DNS 配置把流量重定向回去即可。
已成功割接并产生新数据后:必须把数据从云环境恢复到本地。可选方案有三种:
- Fail-forward:用 AWS DMS 建立 fail-forward 数据库,把数据复制到新的本地数据库;出问题回滚到指定的 fail-forward 库,而不是陈旧的本地库。
- Dual write(双写):应用改造形成两条链路同时写主备库,应用层阻塞等待两库都写入才返回。
- 本机备份与还原:割接前在非生产环境做一次备份还原测试,评估真实还原时间。
回滚计划要写清楚:触发回滚的检查点、回滚策略(回滚怎么执行、数据怎么处理)、以及谁有权决定 fix forward 还是 rollback 的联系人。
PgDog 的流量割接机制
PgDog 的 Traffic cutover 是实验性功能:在源库与目标库已通过逻辑复制同步的前提下,把应用的读写查询迁到目标库。命令在 admin database 上执行 CUTOVER [];,不带 task_id 就切换第一个运行中的复制任务;用 SHOW TASKS 查到 ID 后执行 CUTOVER 12。典型情况整个过程不到一秒,应用不应报错或停机。
前置条件是所有应用都必须通过 PgDog 连接数据库。直连或其他代理连接的应用收不到割接信号,会继续向源库写,造成 split-brain。另外 CUTOVER 只用于手动 resharding(COPY_DATA / REPLICATE),用 RESHARD 命令时会自动执行割接;且只对通过 admin database 启动的迁移生效,用 pgdog data-sync CLI 启动的迁移跑在独立进程里,不能用 CUTOVER 自动切换。手动切换 CLI 迁移的路径是:复制追平后交换 pgdog.toml 与 users.toml 里的 source/destination,对 serving 实例执行 RELOAD,然后停 CLI;期间用 MAINTENANCE 暂停写入以避免丢失在途事务——这条路径没有自动回滚。
自动流程分六步:
- Pause queries:打开 maintenance mode,暂停配置中所有数据库的所有查询,客户端查询缓冲在各自的 TCP 连接里,看起来像冻结。触发暂停的复制延迟阈值是
cutover_traffic_stop_threshold = 1_000_000(默认 1MB)。 - Synchronize databases:逻辑复制流把剩余事务排空到目标库,复制延迟降到零。
- Swap the configuration:交换 source/destination,效果等同于对两个配置文件跑
sed s/source/destination/g,客户端无感知。交换在内存中完成,可用cutover_save_config = true写盘(备份为pgdog.bak.toml/users.bak.toml)。阈值有两个:cutover_replication_lag_threshold = 0(0 bytes)与cutover_last_transaction_delay = 1_000(1 秒)。 - Timeout:
cutover_timeout = 30_000(30 秒)、cutover_timeout_action = "abort"(或"cutover")。阈值内没达到就中止并恢复源库流量;设为"cutover"会强制切到目标库,只在数据完整性不关键或已确认两库一致时使用。 - Reverse replication:在恢复查询前,从新库反向建立逻辑复制流回到原库,把新库的写回灌到源库保持同步,以便回滚。反向复制在两边都暂停时创建,不需要额外复制数据。
- Resume queries:关掉 maintenance mode,流量切到目标库(此时它成为新的 source),割接结束。
这里有个容易被忽略的点:cutover_replication_lag_threshold = 0 并不等于延迟真的能归零。vacuum 事务和非 publication 表的事务都会让延迟指标始终挂着一点点,所以 PgDog 才需要第二个触发条件 cutover_last_transaction_delay = 1_000 兜底——单看延迟归零,割接永远等不到。
割接后,反向复制流作为后台任务运行(SHOW TASKS 里 type=replication)。回滚就是对反向复制任务执行 CUTOVER ,原子交换回来,流量回到原集群;因为反向流一直保持同步,不会丢数据,SHOW TASKS 会显示 rolling back。收尾时,确认新集群健康且不再需要回滚后执行 STOP_TASK ,优雅结束反向流并删除复制槽,Postgres 恢复 WAL 回收,此时回滚不再可能。
反过来也要注意:反向后复制任务一直不停,它持有的 permanent replication slot 会阻止新集群回收 WAL,磁盘会持续堆积。割接完成后的观察窗口里,这条后台任务的存留时间要有人负责收口。
切换 checklist
1. 清点连接入口:所有应用、定时任务、后台脚本、BI 只读账号都经过 PgDog,排除直连(否则 split-brain)。
2. SHOW TASKS 记录复制任务 ID,确认正向复制延迟在阈值附近波动而非持续上涨。
3. 打开 maintenance mode,暂停写入;确认在途事务已被复制流排空。
4. 触发 CUTOVER [],观察 cutover_replication_lag_threshold 与 cutover_last_transaction_delay 两个条件是否满足。
5. 确认配置 swap 完成(必要时 cutover_save_config = true 已落盘),应用无报错。
6. 确认反向复制流已建立(SHOW TASKS 出现 type=replication 的新任务)。
7. 关闭 maintenance mode,恢复查询,观察错误率与延迟。
8. 路由/DNS 侧核对:确认流量没有残留指向旧端点。
9. 割接后功能与非功能验证,记录结果,标记迁移完成。
10. 观察窗口内确认新集群健康,执行 STOP_TASK ;同时确认 WAL 回收恢复正常、磁盘不再增长。
11. 若需回滚:在 STOP_TASK 之前对反向任务执行 CUTOVER ,确认显示 rolling back。