我构建了零停机时间的数据库迁移流水线(PostgreSQL 到 Aurora)

发布: (2025年12月4日 GMT+8 11:22)
8 min read
原文: Dev.to

Source: Dev.to

问题

去年,我接手了一个运行在自托管 PostgreSQL 上的项目。数据库已经膨胀到 500 GB,维护工作(打补丁、备份、复制问题等)耗时太多。于是决定迁移到 Aurora PostgreSQL,以获得托管运维、更好的可扩展性以及原生的 AWS 集成。

难点在于:这是一套服务于 50,000 名日活用户的生产数据库。任何停机都会导致收入损失和客户不满。业务方给我们的维护窗口是……零分钟。压力山大。

我最初的尝试(以及为什么没成功)

我最初的想法很简单:在业务低谷期使用 pg_dumppg_restore。经典做法,对吧?

# The naive approach
pg_dump -Fc production_db > backup.dump
pg_restore -d aurora_target backup.dump

对于 500 GB 的数据库,这大约需要 4–6 小时,取决于网络和实例规格。这意味着在这 4–6 小时内,源库会不断产生新数据,数据会变得陈旧,无法接受。不可接受。

我也尝试了 PostgreSQL 原生的逻辑复制,但在跨 AWS 账户设置发布/订阅并做好安全控制的过程像是一个巨兽。再加上相关运维工具几乎都是自己动手实现的。

随后我发现了 AWS DMS。它可以在托管服务中完成全量加载和变更数据捕获(CDC)。挑战在于围绕它构建一个可重复、安全的迁移流程。

解决方案

我构建了一个完整的迁移框架,包含四大组件:

  • Terraform 模块:用于所有 AWS 基础设施
  • Python 自动化脚本:用于验证和切换
  • GitHub Actions 工作流:实现 CI/CD
  • CloudWatch 监控:提供全方位可观测性

架构概览

Architecture Overview

蓝绿策略的工作原理如下:DMS 先完成全量加载,然后切换到 CDC 模式捕获持续变更。两个数据库保持同步,直到我们准备好切换为止。

Terraform 基础设施

我使用模块化的 Terraform 来实现跨环境的可复用。下面是 DMS 模块的示例:

module "dms" {
  source = "./modules/dms"

  project     = "db-migration"
  environment = "prod"

  subnet_ids         = var.private_subnet_ids
  security_group_ids = [module.networking.dms_security_group_id]

  replication_instance_class = "dms.r5.4xlarge"
  multi_az                   = true

  source_db_host     = var.source_db_host
  source_db_username = var.source_db_username
  source_db_password = var.source_db_password

  target_db_host     = module.aurora.cluster_endpoint
  target_db_username = var.aurora_master_username
  target_db_password = var.aurora_master_password
}

DMS 模块会创建:

  • 具备合适规格的复制实例
  • 带有正确 SSL 配置的源端点和目标端点
  • 启用 CDC 的复制任务
  • 用于监控延迟和错误的 CloudWatch 告警

验证脚本

在任何切换之前,都需要确认数据一致性。我编写了一个 Python 验证工具,支持多维度检查:

# Quick validation (uses table statistics for fast estimates)
python validation.py --quick

# Full validation (exact counts, checksums, sequence values)
python validation.py --full

# Just check DMS status
python validation.py --dms

完整验证会执行以下检查:

检查项描述
行数统计对比源库和目标库的精确行数
校验和对每个表的抽样数据计算 MD5 哈希
序列验证序列值是否同步
主键确保所有表都有主键(CDC 必须)
DMS 状态任务运行状态、复制延迟是否低于阈值

校验和验证代码片段:

def calculate_checksum(self, table: str, columns: list, limit: int = 1000) -> str:
    """Calculate MD5 checksum of sample rows."""
    cols = ", ".join(columns)
    query = f"""
        SELECT md5(string_agg(row_hash, '' ORDER BY row_hash))
        FROM (
            SELECT md5(ROW({cols})::text) as row_hash
            FROM {table}
            ORDER BY {columns[0]}
            LIMIT {limit}
        ) t
    """
    result = self.execute_query(query)
    return result[0][0] if result else None

切换流程

切换是最让人紧张的环节。我构建了一个多阶段流程,并在每个阶段提供自动回滚能力:

阶段操作是否支持回滚
1预验证(检查 DMS、行数)
2等待同步(CDC 延迟低于阈值)
3排空连接(终止源库连接)
4最终同步(等待剩余变更)
5停止复制仅手动
6后置验证仅手动

切换脚本在每个阶段后会将状态保存为 JSON,若出现故障可以从中断点恢复:

# Always do a dry run first
python cutover.py --dry-run

# Execute when ready
python cutover.py --execute

# Resume from saved state if interrupted
python cutover.py --execute --resume

GitHub Actions 集成

整个过程通过 GitHub Actions 自动化。生产环境的切换工作流需要手动批准:

jobs:
  approval:
    name: Approve Cutover
    runs-on: ubuntu-latest
    if: github.event.inputs.mode == 'execute'
    environment: prod-cutover  # Requires manual approval
    steps:
      - name: Cutover Approved
        run: echo "Cutover approved"

  cutover:
    name: Database Cutover
    needs: [approval]
    # ... actual cutover steps

工作流会从 AWS Secrets Manager 拉取凭证,执行切换脚本,上传状态制品用于审计,并在失败时发送 SNS 通知。

结果

迁移成功完成,关键指标如下:

指标数值
总迁移数据量512 GB
全量加载耗时3 小时 22 分钟
切换期间 CDC 延迟2.1 秒
应用停机时间0 秒
数据验证错误0

迁移后我们观察到:

  • 读取延迟下降 40 %(Aurora 只读副本)
  • 数据库维护时间为零
  • 自动备份与时间点恢复(PITR)

经验教训

  1. 彻底测试验证脚本。 最初我在校验和查询中没有正确处理 NULL,幸好在预演环境捕获到了。
  2. 为 DMS 实例合理选型。 我们最初使用 dms.r5.2xlarge,全量加载时 CPU 达到上限。升级到 4xlarge 后迁移时间减半。
  3. 严密监控 CDC 延迟。 我为延迟超过 30 秒设置了 CloudWatch 告警。迁移过程中一次批处理导致延迟飙至 45 秒,及时发现让我们推迟切换,避免风险。
  4. 拥有并演练回滚方案。 我们在切换后保留源 PostgreSQL 运行 48 小时。出现与迁移无关的轻微 bug 时,能够快速回滚,给团队带来安心。
  5. 沟通要比想象的多。 我们每小时发送一次进度更新。
Back to Blog

相关文章

阅读更多 »

🌑 进入黑暗:Soulbound Codex

演示图片 https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2...