Erdmates Logo
Erdmates
返回博客
数据库迁移零停机Schema变更DevOps

数据库迁移策略:零停机变更的工程实践

介绍在不中断生产服务的前提下安全执行数据库Schema变更的各种策略,包括展开-收缩模式、蓝绿部署和分阶段迁移。

2026年4月3日8 分钟阅读

数据库迁移的挑战

每次数据库Schema变更都潜藏着风险。与代码部署不同,数据库变更往往难以快速回滚——已删除的列无法"撤销部署",已迁移的数据无法简单地"还原"。在有用户实时使用的生产系统上执行变更,既要保证数据一致性,又不能影响服务可用性,这是每个工程团队都面临的挑战。

理解两种基本迁移类型至关重要:

  • 向后兼容变更:如添加可为NULL的新列,新旧版本代码都能正常工作
  • 破坏性变更:如重命名列、修改数据类型,需要特殊处理

展开-收缩模式(Expand-Contract Pattern)

展开-收缩是处理破坏性变更最安全的模式,将一次高风险变更拆分为多个低风险步骤:

场景:将 users.full_name 拆分为 first_name 和 last_name

第一阶段:展开(添加新列,保留旧列)

-- 迁移脚本1:添加新列,保持兼容性
ALTER TABLE users ADD COLUMN first_name VARCHAR(100);
ALTER TABLE users ADD COLUMN last_name VARCHAR(100);

-- 回填现有数据
UPDATE users
SET first_name = split_part(full_name, ' ', 1),
    last_name  = NULLIF(
                   trim(substring(full_name FROM position(' ' IN full_name))),
                   ''
                 )
WHERE full_name IS NOT NULL;

-- 创建触发器保持同步(过渡期间新旧列同时维护)
CREATE OR REPLACE FUNCTION sync_name_columns()
RETURNS TRIGGER AS $$
BEGIN
  IF NEW.full_name IS DISTINCT FROM OLD.full_name THEN
    NEW.first_name := split_part(NEW.full_name, ' ', 1);
    NEW.last_name  := NULLIF(trim(substring(NEW.full_name FROM position(' ' IN NEW.full_name))), '');
  ELSIF NEW.first_name IS DISTINCT FROM OLD.first_name
     OR NEW.last_name  IS DISTINCT FROM OLD.last_name THEN
    NEW.full_name := trim(NEW.first_name || ' ' || COALESCE(NEW.last_name, ''));
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER users_name_sync
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION sync_name_columns();

第二阶段:部署使用新列的应用代码,观察稳定运行后进入收缩阶段。

第三阶段:收缩(移除旧列)

-- 确认新代码完全稳定后,移除同步触发器和旧列
DROP TRIGGER IF EXISTS users_name_sync ON users;
DROP FUNCTION IF EXISTS sync_name_columns();
ALTER TABLE users DROP COLUMN full_name;

大表添加列的在线方式

对于亿级记录的大表,ALTER TABLE ADD COLUMN 配合默认值会锁表并重写全表,可能导致长时间服务中断。

-- 低效:锁表重写(百万行可能需要几分钟)
ALTER TABLE orders ADD COLUMN priority INT NOT NULL DEFAULT 0;

-- 高效方式1:先添加可NULL列,再异步回填,最后添加约束
ALTER TABLE orders ADD COLUMN priority INT;  -- 瞬间完成,无锁
-- 异步分批回填(避免一次性锁定大量行)
DO $$
DECLARE
  batch_size INT := 10000;
  offset_val BIGINT := 0;
  updated    INT;
BEGIN
  LOOP
    UPDATE orders SET priority = 0
    WHERE id IN (
      SELECT id FROM orders WHERE priority IS NULL
      ORDER BY id LIMIT batch_size
    );
    GET DIAGNOSTICS updated = ROW_COUNT;
    EXIT WHEN updated = 0;
    PERFORM pg_sleep(0.1);  -- 控制写入速率
  END LOOP;
END $$;

-- 回填完成后,在线添加约束(PostgreSQL 12+支持NOT VALID跳过历史数据验证)
ALTER TABLE orders
  ALTER COLUMN priority SET DEFAULT 0,
  ALTER COLUMN priority SET NOT NULL;

蓝绿数据库部署

对于需要重大重构的迁移,蓝绿部署提供了最安全的切换路径:

-- 阶段1:在绿库(新结构)建好Schema
-- 阶段2:使用逻辑复制将蓝库数据同步到绿库
-- PostgreSQL逻辑复制配置示意
-- 在蓝库(源)执行:
CREATE PUBLICATION blue_to_green FOR ALL TABLES;

-- 在绿库(目标)执行:
CREATE SUBSCRIPTION green_subscription
  CONNECTION 'host=blue-db port=5432 dbname=myapp user=replication_user'
  PUBLICATION blue_to_green;

-- 等待绿库与蓝库同步(lag接近0)
SELECT * FROM pg_stat_subscription;
-- 阶段3:应用层切换连接到绿库,观察无误后关闭蓝库

迁移版本管理

无论使用Flyway、Liquibase还是自定义迁移框架,都应遵循以下原则:

-- 创建迁移历史跟踪表
CREATE TABLE schema_migrations (
  version     VARCHAR(20) PRIMARY KEY,
  description VARCHAR(200) NOT NULL,
  applied_at  TIMESTAMP NOT NULL DEFAULT NOW(),
  applied_by  VARCHAR(100) NOT NULL DEFAULT current_user,
  checksum    VARCHAR(64)  -- 防止已执行的迁移文件被修改
);

-- 每次迁移前记录
INSERT INTO schema_migrations (version, description, checksum)
VALUES ('20260403001', 'split_users_fullname', 'sha256_of_migration_file');

迁移文件命名规范:V20260403001__split_users_fullname.sql,时间戳前缀确保执行顺序,每个迁移文件只做一件事,且必须幂等(可重复执行不报错)。

使用Erdmates规划迁移路径

Erdmates的Schema可视化帮助您在迁移前直观对比新旧数据库结构的差异,识别受影响的表和关联关系,制定周密的展开-收缩计划。将迁移前后的ERD分别保存,便于团队评审和回顾。

安全的数据库迁移需要耐心和纪律:宁可多分几个步骤,也不要冒险一次性完成所有变更。每一步都应该可验证、可回滚,这才是成熟工程团队的数据库变更文化。

立即使用Erdmates开始

使用AI驱动的ERD编辑器开始数据库设计。免费使用。

免费试用演示免费开始

相关文章

bigint、UUID v4 还是 UUID v7?主键选择指南(含 PostgreSQL 18 的 uuidv7())

比较 bigint、UUID v4 和 UUID v7 主键的优缺点,介绍 PostgreSQL 18 内置的 uuidv7() 带来的变化,以及不同场景下的选择方法。

被AI代理读取的数据库:MCP时代的模式设计原则

AI代理通过MCP直接连接数据库的情况越来越多。本文整理如何设计名称、注释和约束,让代理不会误解模式,以及如何安全地划分权限。

PostgreSQL 19预览:影响模式设计与运维的变化(基于Beta 4)

以官方发布说明(Beta 4)为准,整理即将正式发布的PostgreSQL 19的变更中直接影响表设计、迁移和运维的内容。同时介绍在Beta阶段被撤回的功能,以及升级前需要确认的行为变化。

Erdmates Logo
Erdmates

使用自然语言设计数据库,通过AI驱动的ERD编辑器与团队实时协作。

服务

  • 功能介绍
  • 价格方案
  • 演示
  • SQL 转 ERD

资源

  • 博客
  • 帮助
  • 联系我们

法律声明

  • 服务条款
  • 隐私政策
  • 第三方提供
公司名称: Produx|代表人: 최정회
地址: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
电子邮件: master@erdmates.com
隐私保护负责人: 최정회|托管服务提供商: Amazon Web Services

© 2026 Produx. 版权所有。