幕后揭秘:架构回滚是如何工作的
今天,我们发布了一项新功能,这项功能允许您在不丢失在部署和回滚之间写入到原始架构的数据的情况下,立即回滚最近部署的架构更改。
这不仅仅是一个简单的回滚功能。
想象一下,你刚刚向生产数据库部署了一项架构更改,而这项更改从某张表中删除了一列。随后,你在浏览器中刷新生产环境应用程序,却惊恐地发现应用程序崩溃了。
与其慌乱地将数据库回滚到部署之前的状态并可能在这个过程中丢失数据,不如使用我们的新功能,只需点击一个按钮即可“撤销”这次架构更改。如果在这种错误的架构修改生效期间有数据写入,我们会在您回滚之后自动保留这些数据。
乍一看,这或许像是科幻小说,然而这是真实的,并且实现这一功能需要大量复杂的技术支持。本文将揭示该功能背后的技术原理,以及我们是如何构建它的。
文章末尾将通过一个实例,展示从部署到回滚整个过程中实际发生了什么。
在线架构更改工具如何工作?
要理解如何实现迁移回滚,首先需要了解在线架构更改工具的工作原理。
所有在线架构更改技术均遵循一个通用的高层次模式:
- 它们不会直接修改生产表,而是创建一个空的“影子表”,它的架构与生产表相同,但没有数据。
- 它们在影子表上执行架构更改操作。这是一个廉价操作,因为影子表没有数据。
- 它们将原始表数据复制到影子表,同时追踪新的变化并应用至影子表。
- 最后,当两个表的内容完全同步后,工具会进行切换:将原始表替换为影子表。
简而言之,在线架构工具的工作流程是:复制生产表但不包含数据,在副本上应用架构更改,同步数据,然后交换两个表。
在 PlanetScale,我们利用 Vitess 的核心组件之一 VReplication 的底层能力来实现在线架构更改。VReplication 是 Vitess 底层数据库系统的核心部分,负责支持在线架构更改、迁移回滚、无停机的数据导入等功能。
了解了在线架构更改工具的工作原理后,我们可以深入了解 VReplication 的独特实现方式,它正是支持迁移回滚的关键。
VReplication 架构更改
一些在线架构更改工具的实现方式有所不同。VReplication 特别具有以下核心设计特点:
- 它不仅追踪回填数据(现有数据),还追踪持续变更(新写入的数据)。
- 它使用精准逻辑将每个事务(完整的数据库操作)与变更期间的数据库位置(使用 MySQL GTID,全局事务标识符)进行映射。基于事务开始的时间点,能够精确地追踪两个表之间的现有与新变更。
- 它基于这些位置标记,在数据复制状态和持续变更追踪之间进行切换。
- 它将数据复制状态及其进度事务性地耦合在一起,同样将变更日志事件及其进度事务性地耦合在一起。
- 与其他架构更改解决方案不同的是,Vitess 的迁移在完成后不会终止。这一点在架构回滚时尤为重要。
总而言之,VReplication 拥有一个事务性准确的迁移状态日志,随时知道哪些行已经被复制,以及哪些变更日志事件已经被处理。这种特性在在线架构更改工具领域中独树一帜,正是这一特性使我们能够在不丢失数据的情况下,立即回滚架构更改。
VReplication 如何实现架构回滚?
让我们结合 VReplication 的在线架构更改流程,再通过一个示例来理解回滚的过程。
假设你在部署请求中执行以下语句:
SQL1ALTER TABLE users DROP COLUMN title;
部署后,以下是幕后实际发生的内容:
步骤1
我们首先复制生产环境中的 users
表(原始表)的架构,但不包含数据。得到的就是一个影子表,它保留了 title
列。
步骤2
我们在影子表上执行 ALTER TABLE users DROP COLUMN title
语句,将 title
列从架构中移除。
步骤3
我们开始将生产表中的数据复制到影子表。
步骤4
我们持续复制生产表的数据,包括新写入的数据,以确保两个表最终同步。
同步这两个表是一个巨大任务,尤其是在这个过程中生产表继续新增数据。例如,当一行数据被复制到影子表后,可能会有新的写入更新了那行的内容。这些更新会写入生产表,因此导致生产表和影子表再度不同步。
这是 VReplication 的强大之处。它通过间断性地批量复制现有数据与新数据,优雅地解决了这一问题。
步骤5
当我们开始复制一组现有的行时,我们会运行 START TRANSACTION WITH CONSISTENT SNAPSHOT
,这一步会锁定一个一致性快照,在复制过程中模拟冻结时间,保证复制的准确性。这是通过捕获事务级别的状态 GTID
(全局事务标识符)完成的。
步骤6
一旦完成了现有行的复制,我们就会切换到复制新的写入数据。我们只关心那些满足两个条件的数据:这些数据在之前的 GTID 点之后写入,并且包含对影子表中的数据的更新。
步骤7
完成一批写入事件处理后,我们捕获一个新的 GTID,然后回到复制现有数据的过程。
步骤8
最后,当数据完全复制后,我们进行强制停止,这个过程称为切换时期(cut-over period)。
切换时期
迁移过程中的最后一步,也是最关键的一步,是切换时期(cut-over period),即将原始表替换为影子表。这也是唯一会在表上施加写锁的步骤。在交换完成之前无法写入。
写锁的作用是确保两个表处于完全同步状态。但从应用的角度来看,我们不会停机。我们将在换表期间暂存所有新的写入,并在交换完成后应用这些写入。
Vitess 的迁移流程在切换点标记了数据库的位置,然后交换两个表:影子表取代原始表,而原始表则变成影子表。此时,两张表是完全同步状态。
迁移过程完成后,新的生产表开始接收流量。但此刻故事并未结束,我们仍保留原始表以及 VReplication 状态。接下来会立即用到它们。
为回滚做准备
迁移完成后,PlanetScale 在后台准备一个开放式的回滚进程,并开始追踪新写入的数据,将这些更新应用到影子表。
这应该很熟悉,因为我们在迁移过程中已经创建了影子表,它已被完整填充并与新表完全同步。
因此,在上述示例中,部署完成后,如果您发现需要回滚,可以执行以下步骤:
步骤9
点击“回滚变更”。
在表切换到生产状态后到您点击“回滚”之间的时间内,我们已经在后台准备好回滚过程。我们交换的原始生产表现在重新变回影子表。
步骤10
影子表已经包含完整的数据和旧的架构,不需要重新执行复杂的数据复制流程。我们只需追踪新的写入,这已经在后台默默完成。
最后,只需再次交换表,影子表重新成为生产表,生产表变回影子表。
此时您会重新获得原始架构,users
表的 title
列得以恢复,并且您的应用程序也可以正常工作。在此过程中,您还保留了回滚期间新增的数据,从而避免了传统回滚和恢复方法中丢失数据的麻烦。
需要注意的是,回滚后新数据可能与原始架构不完全一致,例如在表切换后新增的一些记录无法恢复被删除的 title
值。这是预期情况,您可以根据需要进行后续清理。
除了简单的 ALTER
语句,还有更多关于回滚 CREATE
和 DROP
的细节问题,敬请期待未来的博客文章。
总结
希望这篇文章能帮助您理解我们架构回滚功能的工作原理。
我们的目标是通过提供可扩展和易用的数据库解决方案,持续改善开发者的体验。请记住,所有的回滚操作只需点击一个按钮,在幕后即可在短短几秒内完成。
如果您想亲眼看到此功能的运行,可在数据库设置页面中注册参与我们的限量测试版。有关更多信息,请参阅我们的部署请求文档中的“回滚架构更改”章节。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接