零停机时间的 Laravel 数据库迁移
提示 PlanetScale 允许你像管理代码分支一样管理数据库分支。在本文中,我们会同时讨论 数据库分支 和 **应用代码分支**。为了保持清晰,我们将 PlanetScale 数据库分支称为 “PlanetScale 分支”,将应用代码分支称为 “代码分支”。
部署时运行迁移的问题
在许多 Laravel 工作流中,部署脚本包含了 php artisan migrate
,该命令在每次部署时运行新的迁移操作。例如,以下是 Forge 在推送到生产环境时使用的默认快速部署流程:
- 进入站点的目录
- 运行
git pull
- 运行
composer install
- 运行
php artisan migrate
直接对生产环境数据库执行架构更改(如 ALTER
、CREATE
等),被称为 **直接 DDL(Data Definition Language)**。直接 DDL 是危险的,因为其可能会锁定你的表,从而使表完全不可访问(包括读取)。直接 DDL 既没有速率限制,也没有有效的隔离,更糟糕的是,它缺乏回滚策略,如果强制回滚可能会导致更多锁问题,甚至数据丢失。
警告 在生产环境中运行 php artisan migrate
是危险的,因为它可能锁定数据库,阻止读写操作。
为了更好地理解这种风险,让我们简要看一下 MySQL 的锁定机制。
MySQL 的锁定机制
为了执行事务(例如 ALTER TABLE
语句),MySQL 有时需要锁定表以保证事务隔离性。例如,如果你部署了一个架构更改,增加了 varchar
列的大小,整个表可能会被临时锁定,以完成该事务。这意味着在操作进行时,没有人可以访问该表(包括读写)。
MySQL 具有不同类型的锁,具体类型和场景取决于操作的类型。
如何避免锁定并实现非阻塞的架构更改
在线架构更改工具 可以帮助避免表锁定。与直接对表应用更改不同,此工具采用以下流程:
- 创建表的副本(称为影子表)。
- 应用架构更改到影子表。
- 同步两个表中的数据。
- 原子性替换表(将影子表替换为主表)。
- 删除旧表。
PlanetScale 通过其分支工作流自动处理以上过程。
PlanetScale 的工作流
PlanetScale 的非阻塞架构更改工作流
PlanetScale 工作流步骤
当你需要进行架构更改时,可以按照以下步骤:
- 从你的生产架构创建一个 PlanetScale 开发分支(数据库的隔离副本)。
- 在 PlanetScale 开发分支中引入更改。
- 完成架构更改后,创建 **部署请求(Deploy Request)**。
- 团队可以审查并批准部署请求。
- 点击 “部署” 按钮后,更改将加入部署队列。
此时在线架构更改工具介入,开始处理。
例如,你在 Laravel 应用中添加了一项迁移,以运行以下 SQL,将 description
列的大小增加到 300:
ALTER TABLE posts ALTER COLUMN description VARCHAR(300);
当 PlanetScale 通过部署请求应用此迁移时,它会将现有的 posts
表复制到一个新的影子表,更新 description
列,并确保两个表同步。然后,启动替换操作,将两个表交换位置。此流程确保原始表永远不会被锁定。
迁移运行的时机
现在你已经了解为什么不能在部署期间运行 php artisan migrate
,接下来会问:
“我应该什么时候运行迁移?”
简短回答是:**视情况而定**。以下是两个示例:
示例 1:添加字段
你需要在应用代码中创建一个输入表单,同时需要在表中添加一列。在这种情况下,你需要确保架构在生产环境中已更新后,再上线应用代码。
示例 2:删除字段
你要删除一个表中的现有列。此时需要确保应用代码已停止向该列写入数据后,再上线架构更改。
你可以看到,架构更改的类型决定了是应该在应用代码发布前还是发布后运行迁移。
下面为每种情况提供了一份步骤清单,可根据需要为你的应用逐步实现。
关于 Laravel 数据库迁移
简单总结一下:你仍然可以使用 Laravel 数据库迁移修改架构,但只能在应用程序的开发环境中运行。
- 开发环境连接到 **PlanetScale 开发分支**。
- 数据库迁移会在 PlanetScale 开发数据库中运行,待准备好后可安全地合并到生产环境。
不要在生产服务器上运行迁移。 生产服务器连接到 PlanetScale 的主生产数据库,PlanetScale 已通过部署 PS 开发分支到生产时处理了这些迁移。
另需注意:如果你尝试在生产环境运行迁移,操作会失败。为保护生产环境,PlanetScale 不支持在生产分支上直接执行 DDL,除非你禁用安全迁移。但禁用安全迁移可能导致表锁定,进而引发停机。
整体架构更改工作流
以下覆盖了添加字段/表、删除字段/表以及更改字段/表名称的架构更改步骤。
1. 添加字段或表
场景:确保生产架构更新完成后,应用代码开始向新字段/表写入数据。 步骤:
- 在 Laravel 应用中创建代码开发分支。
- 创建 Laravel 数据库迁移以修改架构。
- 创建 PlanetScale 开发分支。
- 将 Laravel 的代码开发分支连接到 PlanetScale 开发分支。
- 运行 Laravel 数据库迁移以在 PlanetScale 开发分支中进行架构更改。
- 部署 PlanetScale 架构更改请求(进入生产数据库)。
- 完成后,部署应用代码以向新字段写入数据。
2. 删除字段或表
场景:删除字段/表的架构更改需要更新应用代码,确保代码不再使用该字段/表后再上线架构。 步骤:
- 按上述流程完成前5步。
- 部署代码更新以确保不再向删除字段/表写入数据。
- 部署 PlanetScale 的请求以删除字段/表。
3. 更改字段名或表名
场景:根据多步流程避免直接更改名称(通过克隆字段或表实现)。 步骤:
- 按上述流程完成前5步。
- 部署 PlanetScale 数据库迁移以添加新名称表到生产数据库。
- 部署代码更新,同时向新表和旧表写入数据,但仅从旧表读取数据(等待数据同步)。
- 运行脚本,将现有数据从旧表复制到新表。确保两表同步后,更新代码读取新表,放弃旧表。
- 确认旧表不再使用后,部署 PlanetScale 请求以安全地删除旧表。
提示 可以使用 PlanetScale Insights(一款仪表板内的查询监控工具)调查表是否已停止使用。
加分项:在 Laravel 中回滚架构更改
通过在线架构更改方法,可立即回滚架构更改。如果你部署了错误的架构变更,可在仪表板中点击“回滚”按钮,在 30 分钟内撤销更改。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接