数据库迁移策略:如何在不破坏生产环境的情况下更改模式

数据库模式迁移是Web应用程序开发中风险最高的操作之一——执行不当的迁移可能导致生产宕机、数据丢失或数小时的停机。以下是有效的模式。

为什么数据库迁移是危险的

生产中的数据库是实时的——当你试图更改模式时,正在写入新数据。危险:如果你添加一个没有默认值的NOT NULL列,现有行是无效的;如果你重命名一列并分别部署应用程序代码和数据库迁移,其中一个将读取一个尚不存在的列。在规模上,对5000万行表的ALTER TABLE可能锁定表数分钟,随着应用程序请求排队而造成级联故障。根本约束:在大多数部署系统中,数据库迁移和应用程序代码无法原子性地部署——有一个窗口期,要么是新代码针对旧模式运行,要么是新模式在新代码部署之前存在。

扩展-收缩模式

扩展-收缩(也称为并行更改或绞杀者迁移)模式是零停机模式更改最安全的方法。它有三个阶段:第1阶段(扩展):添加新模式元素而不删除旧元素。添加新列(带有默认值或可为空);开始写入旧列和新列;开始将旧数据回填到新列。旧应用程序代码仍然有效,因为旧列仍然存在。第2阶段(迁移):一旦所有行都被回填且两列同步,部署新应用程序代码,从新列而不是旧列读取。第3阶段(收缩):删除旧列。旧应用程序代码不再部署,因此没有内容读取旧列;迁移完成。时间表:如果通过扩展-收缩安全地完成,看起来即时的列重命名在生产中可能需要数周。对于零停机来说,这是正确的权衡。

特定的安全迁移技术

添加NOT NULL列:永远不要在大表上的单次迁移中添加没有默认值的NOT NULL列。安全方法:首先,将列添加为可为空(ALTER TABLE users ADD COLUMN new_field VARCHAR(255));然后回填值;然后添加NOT NULL约束;然后添加应用程序级别验证。对于PostgreSQL 11+,添加带有非易失性默认值的列是O(1)——它不重写表。重命名列:扩展-收缩。永远不要一步直接重命名。添加索引:在PostgreSQL中,使用`CREATE INDEX CONCURRENTLY`——这在后台构建索引而不锁定表。在MySQL/MariaDB中,pt-online-schema-change或gh-ost(GitHub的在线模式更改工具)允许在大表上创建索引而无需锁定。删除列:只有在所有应用程序代码都删除了该列之后才安全。常见错误:从ORM模型中删除列而没有数据库迁移,导致ORM选择`*`并在该列仍在数据库中时崩溃。先从ORM中删除(部署),然后删除列。

迁移工具和实践

工具:Flyway和Liquibase是Java生态系统标准;Alembic用于Python/SQLAlchemy;Rails ActiveRecord迁移;Prisma Migrate用于Node.js/TypeScript。所有这些都提供带有up/down操作的版本化迁移文件。关键实践:永远不要编辑已经在任何环境中运行的迁移——创建新迁移来修复它;在生产中运行之前在生产大小的数据克隆上测试迁移(性能取决于数据集大小);始终有回滚计划(不仅仅是`down`迁移——有时回退到之前的代码版本就是回滚);在生产中运行迁移时监控它,并准备在导致意外锁定时终止它。

上一篇 Database Migration Strategies: How to Change Your Schema Without Breaking Production
下一篇 Wine Regions of Tuscany: What Each One Actually Produces