数据库迁移:概念和原则(第 2 部分)

Last reviewed 2024-03-11 UTC

本文档讨论如何设置和执行数据库迁移过程,包括故障场景。本文档为两部分文章中的第 2 部分。第 1 部分为需要将数据库从本地或其他云环境迁移到 Google Cloud 的云架构师介绍了近零停机时间数据库迁移概念、原则和术语。

数据库迁移设置

本部分介绍数据库迁移的几个阶段。首先,您需要设置迁移。然后,在完成迁移并将客户端切换到目标数据库后,您可以移除源数据库,或者在切换后发现迁移存在问题时,在必要的情况下实施回退计划。回退计划有助于确保业务连续性。

在迁移过程中,您需要特别注意可能会引入的任何架构或数据更改。如需详细了解这些更改可能产生的影响,请参阅本文档后面的迁移期间的动态更改

目标架构规范

对于每个目标数据库系统,您需要定义并创建其架构。对于同构数据库迁移,如需更快地创建此规范,您可以将源数据库架构导出到目标数据库,从而创建目标数据库架构。

架构的命名方式非常重要。一种方法是匹配源架构和目标架构名称。但是,尽管这简化了客户端切换,但如果工具同时连接到源数据库架构和目标数据库架构(例如,为了比较数据),此方法可能会让用户感到困惑。如果您使用配置文件抽象化架构名称,则为目标数据库架构提供与源数据库架构不同的名称可以更轻松地区分架构。

在进行异构数据库迁移时,您需要创建每个目标数据库架构。此工程过程可能需要多次迭代。在实现迁移之前,您可能需要进一步更改架构以适应迁移过程和任何数据修改。

由于在测试和执行迁移时可能会多次创建目标数据库,因此创建架构的过程需要可重复执行(最好可通过安装脚本执行)。您可以使用代码管理系统来控制脚本的版本、确保一致性以及访问脚本的更改历史记录。

查询迁移和执行语义

最终,您需要将客户端从访问源数据库系统切换为访问目标数据库系统。在同构数据库集成中,如果未修改架构,则查询可保持不变。虽然客户端必须在目标数据库系统上进行测试,但由于查询而无需修改客户端。

对于一般性的异构数据库迁移,您必须修改查询,因为源数据库和目标数据库之间的架构不同。差异可能是源数据库和目标数据库之间的数据类型不匹配。此外,并非源数据库系统中所提供的查询语言的所有功能都可在目标数据库系统中使用,或反之亦然。在极端情况下,您可能需要将一个针对源数据库系统的查询转换为多个针对目标系统的查询。在反向场景中,目标数据库中提供的查询语言功能比源数据库多,您可能需要将多个针对源数据库的查询合并为一个针对相应目标数据库的查询。

查询的语义也可能不同。例如,某些数据库系统会立即在事务内具体化事务更新,因此在读取同一数据项时,系统会检索更新后的值。其他系统不会立即具体化更新,而是等到事务提交。如果源数据库系统上的逻辑依赖于写入的具体化,则目标数据库上的相同逻辑可能会导致数据不正确甚至失败。

如果必须迁移查询,则需要测试所有功能,以确保客户端在迁移前后的行为相同。您还可以在数据级别进行测试,但此类测试无法取代在客户端级别进行的测试。客户端从业务逻辑角度执行查询,并且只能在业务逻辑级别进行测试。

迁移过程

对于异构数据库迁移,迁移过程会指定如何修改从源数据库系统提取的数据并将其插入目标数据库。从源数据库中提取数据项并将其传输到目标数据库时,系统会定义并执行数据修改(例如本文档的数据更改中讨论的修改)。

对于同构数据库迁移,如果源数据库和目标数据库的架构相同,则不需要修改数据。数据从源数据库提取后,会插入到目标数据库中。

可能需要多种配置,具体取决于您的数据库迁移系统。例如,您必须指定要修改和传输的数据是否必须在数据库迁移系统中间歇性存储。存储数据可能会减慢整个迁移过程,但会在出现故障时显著加快恢复速度。您可能需要指定验证类型。例如,一些数据库迁移系统会查询源系统和目标系统,以建立在该查询时间点之前所迁移数据集的等价数据集。错误处理要求您指定故障恢复行为。同样,此要求取决于正在使用的数据库迁移系统。

毫无疑问,您需要反复全面测试数据迁移。理想情况下,您的迁移会经过测试,以确保每个已知数据项都已迁移、未发生数据修改错误、性能与吞吐量充足以及迁移可以按时完成。

回退过程

在数据库迁移期间,源数据库会保持运行状态(除非您的迁移涉及计划内停机时间)。如果您的数据库迁移在任何时候失败,您可以(在最糟糕的情况下)中止迁移并将目标数据库重置为初始状态。解决失败问题后,您可以重启数据库迁移。失败问题及其解决过程不会影响处于运行状态的源数据库系统。

如果在数据库迁移完成后发生失败,并且客户端已切换到目标数据库,则失败问题及其解决过程可能会影响客户端,造成客户端无法正常运行。在最佳情况下,失败问题会得到快速解决,因此客户端的停机时间很短。在最糟糕的情况下,失败问题得不到解决,或者解决过程需要很长时间才能完成,而且您必须将客户端切换回源数据库。

如需将客户端切换回源数据库,您必须将目标数据库上的所有数据修改迁移回源数据库。您可以设置此过程,并将此过程作为单独的完整数据库迁移执行。但是,由于此时客户端无法使用目标数据库,因此造成停机时间大幅延长。

在这种情况下,您需要在原始数据库迁移完成后立即启动迁移过程,才能避免客户端停机。对目标数据库系统所做的每项更改都会立即应用于源数据库系统。遵循此方法可确保目标数据库系统和源数据库系统始终保持同步。

准备从目标数据库回退为使用源数据库需要投入大量精力。决定是实现和测试回退过程还是了解不这样做的后果(即停机时间大幅延长)至关重要。

数据库迁移执行

执行数据库迁移涉及五个不同的阶段,本部分将对此进行讨论。第六个阶段是回退阶段,但回退是一种极端情况,被视为正常数据库迁移执行的一种例外。

本部分讨论的过程是接近零停机时间异构数据库迁移。如果会造成停机时间大幅延长,您可以使用备份和恢复或导出和导入方法将前三个阶段(初始加载、继续迁移和排空)合并成一个阶段。

同构数据库迁移具有特殊情况。在这种类型的迁移中,您可以使用数据库管理系统复制功能(针对支持该功能的系统)在源数据库系统保持运行状态时迁移数据。

此处讨论的阶段概述的方法可能需要根据数据库迁移过程的要求进行修改。

第 1 阶段:初始加载

首先,从所有源数据库中迁移所有指定数据。在数据迁移开始时,源数据库具有特定状态,并且该状态在迁移过程中会发生变化。

在发生变化的同时启动迁移有一个诀窍:记下开始提取第一个数据项之前的数据库系统时间。通过此时间戳,您可以从事务日志中获取从该时间开始的所有数据库更改。此外,初始加载必须读取所有数据的一致数据库状态。您可能需要对数据库设置短暂的锁定,才能防止读取不一致的数据集。

此阶段包含以下内容:

  • 在数据库迁移开始之前记下数据库系统时间。
  • 执行初始加载迁移过程,从需要迁移的源数据库和正在迁移数据集的源数据库,查询数据集(无论是完整数据还是部分数据)。在关系型数据库模型中,初始加载迁移过程会执行查询(如 SELECT * 或具有选择和/或投影的查询)。迁移过程会执行在过程中指定的数据修改。

在初始加载迁移过程执行期间,客户端通常会更改源数据库。由于您在启动时记录了数据库系统时间,因此您稍后可以从事务日志中获取这些更改。

初始加载阶段的结果是将初始数据集从源数据库系统完全迁移到目标数据库系统。但是,源数据库和目标数据库尚未同步,因为客户端可能在迁移期间修改了源数据库。第 2 阶段涉及捕获和迁移这些更改。

第 2 阶段:继续迁移

继续迁移有两个目的。首先,读取初始加载启动后在源数据库中发生的更改。接着,它会捕获这些更改并将其传输到目标数据库。

此阶段包含以下内容:

  • 从第 1 阶段中记录的数据库系统时间开始继续迁移过程。迁移将从该时间开始读取事务日志,并将每个更改应用于目标数据库系统。
  • 执行任何数据修改。迁移过程会按照您指定的那样执行此步骤。

在该数据库系统时间之后记录的更改有时会在初始加载期间传输。因此,在继续迁移期间,这些更改可能会再次应用。您需要定义迁移过程,以确保更改不会应用两次(例如,使用标识符来确保)。假设在初始加载期间传输了某个已更改的数据项,并将该插入操作记录在事务日志中。通过将标识符应用于该数据项,迁移系统可以通过事务日志确定不需要再进行一次插入操作,因为该数据项已存在。

继续迁移阶段的结果是目标数据库与源数据库完全同步或几乎完全同步。如果源数据库系统中的更改未迁移,您将拥有一个几乎同步的数据库。

差异可大可小,具体取决于您配置数据库迁移系统的方式。例如,为了提高效率,不是每项更改都应立即迁移,否则,如果对源数据库系统的更改达到峰值,就可能造成源数据库系统负载过重。通常,系统会以批量操作的形式批量收集和迁移更改。对于较小的批次,源数据库和目标数据库之间出现的差异较小,但如果更改很频繁,则源数据库可能会产生更高的负载。

如果批次大小是动态配置的,则最好在继续迁移阶段开始时同步较大的批次,然后在源数据库和目标数据库几乎要达成同步时从大到小同步批次。此方法可提高源数据库和目标数据库之间达成同步的效率,并减少源数据库和目标数据库之间的差异。

第 3 阶段:排空

要准备将客户端从源数据库切换到目标数据库,您必须确保源数据库和目标数据库完全同步。排空是将源数据库中的其余更改迁移到目标数据库的过程。

此阶段包含以下内容:

  • 让源数据库系统处于静默状态。这意味着在源数据库上不会发生数据修改,并且事务日志不会收到其他修改条目。
  • 等待所有更改都迁移到目标数据库。此过程是对更改的实际排空。
  • 排空完成后,备份目标数据库,以便为未来增量备份指定一个起点。

排空阶段的结果是源数据库系统和目标数据库系统已同步,而且不会发生任何数据修改。

为了确保排空已完成,可以将“最后一次插入”数据项写入源数据库。一旦“最后一次插入”数据项出现在相应的目标数据库中,排空阶段就完成了。

第 4 阶段:切换

排空阶段完成后,您可以将客户端从源数据库切换到目标数据库。我们推荐以下最佳做法:

  • 在启用对生产数据库的访问权限之前,请先测试基本功能,以确保客户端正常运行且按预期方式工作。测试用例的数量将决定生产数据库的实际停机时间。
  • 在启用客户端访问之前,请备份目标数据库。这种最佳做法可确保目标数据库的初始状态可恢复。

在切换结束时,客户端将完全正常运行并开始访问生产数据库(在此之前,本文档称之为“目标数据库”)。

第 5 阶段:删除源数据库

完成切换到生产数据库后,您可以删除源数据库。最好对每个源数据库进行最终备份,以确保您可以访问已定义的最终状态。数据法规可能还出于合规性原因要求进行最终备份。

第 6 阶段:回退

实现回退过程(尤其是针对非常重要的数据库客户端)可以很好地防范各种迁移问题。回退类似于迁移,但过程相反。也就是说,通过回退,您可以设置从目标数据库迁移回源数据库的过程。 在异构数据库迁移中,回退过程会更为复杂。因此,我们建议您仅在全面测试数据库迁移过程并且连接到目标数据库的应用满足服务等级协议 (SLA) 之后执行切换。

排空源数据库并备份所有数据库后,您可以启用迁移过程,以识别目标数据库中的更改,并在切换前将其迁移到源数据库。

构建这些迁移过程可确保在客户端更改目标数据库后,源数据库会进行同步,其数据状态会保持最新。切换后的几天或几周内可能需要回退。例如,客户端可能会首次访问功能,并因功能受损且无法快速修复而被屏蔽。在这种情况下,客户端可以切换回访问源数据库。在客户端切换回之前,对目标数据库的所有更改都必须排入源数据库。

在此方法中,某些情况需要特别注意:

  • 您必须设计目标架构,以便能够进行反向迁移(从目标数据库到源数据库)。例如,如果您的初始迁移过程(从源数据库到目标数据库)包含联接或聚合,则反向迁移会非常复杂,甚至不可能完成。在这种情况下,目标数据库中的单个数据也必须可用。
  • 如果源数据库包含事务日志,但目标数据库不提供此类非功能性功能,则可能会出现问题。在这种情况下,反向迁移(从目标数据库到源数据库)必须依赖于差分查询。必须在目标数据库架构中设计和准备该设置。
  • 最初在源数据库上运行的客户端需要保持可用且可正常运行,以便它们可在回退期间启用。对访问目标数据库的客户端所做的任何功能性更改,也必须应用于访问源数据库的客户端,以确保功能等效。

虽然回退是作为最后的办法来使用,但实现回退过程很重要,因此必须视为完整的数据库迁移(包括测试)。

迁移期间的动态更改

通常,数据库是动态系统,因为架构和数据值可能会发生变化。数据库架构可能会因业务需求等因素而异,并且数据值可能会随架构更改而发生变化,也可能独立于架构更改而发生变化。随着应用实现的相应更改,数据值会随时动态地发生变化。以下部分讨论了一些可能的更改及其对数据库迁移的影响。

架构更改

数据库可以归类到需要预定义架构的系统或者无架构系统。通常,需要预定义架构的系统可支持架构更改操作,例如在关系型系统中添加特性或列。

在这些系统中,您可以通过变更管理流程来控制更改。变更管理流程允许以可控方式进行更改。依赖于架构的任何操作(如查询或数据迁移过程)都会同时更改,以确保更改在整体上一致。

不需要预定义架构的数据库系统可以随时更改。架构更改不仅能由已获授权的用户完成,在某些情况下,还能以程序化方式实现。在这些情况下,可以随时更改架构。依赖于架构的操作可能会失败,例如查询或数据迁移过程。为了防止这些数据库系统中的架构发生不受控制的更改,您必须将变更管理流程作为惯例和接受的规则来实现,而不是由系统强制执行。

数据更改

通常,架构会控制各种数据特性的可能数据值。无架构系统对数据值没有任何限制。

在这两种情况下,系统可能会显示之前未存储的数据值。例如,枚举类型通常作为数据库系统中的一组字符串实现。在编程语言级别,这些可以在客户端中作为真实枚举类型实现,但不一定如此。某个客户端可能会视为有效枚举值并进行存储的内容,对于其他客户端而言可能并非是有效的。此外,数据迁移过程可能会切断其功能与枚举值的关联。如果出现新值,数据迁移过程可能会失败。

另一个示例可在 JSON 结构的存储区中找到。在许多情况下,JSON 结构会存储在数据类型字符串中;但是,这些参数在访问时会被解释为 JSON 值。如果 JSON 结构发生更改,则数据库系统不会检测到该更改;将字符串解释为 JSON 值的数据迁移过程可能会失败。

迁移过程更改

在数据库迁移仍在进行期间执行变更管理既困难又复杂,可能会导致数据迁移失败或数据不一致。最好将必需的更改延迟到排空阶段结束,此时源数据库系统和目标数据库系统是同步的。这个时候进行的更改随后将限制于目标数据库及其客户端(除非还实现了回退)。

如果您需要在数据迁移过程中更改迁移过程,我们建议您将更改保持在最低限度,并且可能的话,进行一些小更改,而不是较复杂的更改。此外,您可以考虑先使用来源数据库和目标数据库的测试实例来测试这些更改。理想情况下,您应使用生产数据加载测试源,然后将生产数据迁移到测试目标。使用此方法,您可以在不影响正在进行的生产迁移的情况下验证建议的更改。测试并验证更改后,您可以将这些更改应用于生产系统。

若要在数据迁移仍在进行期间实现更改,您必须能够停止并重启数据迁移系统(可能需要修改数据迁移过程)。在这种情况下,您无需从最初的数据加载阶段开始。如果数据迁移系统支持测试迁移运行功能,您也可以使用该功能。

我们建议您不要在数据迁移期间更改架构、数据值或数据迁移过程。如果您必须进行更改,则可以考虑从头开始重启数据迁移过程,以确保您已指定起始状态。在任何情况下,您都必须使用生产数据进行测试,并在应用更改之前备份数据库,以便在需要时将整个系统重置为一致的状态。

迁移失败缓解措施

数据库迁移过程中可能会出现意外问题。下面重点列出了一些需要预先规划的方面:

  • 吞吐量不足。尽管进行了负载测试,但迁移系统仍可能缺少足够的吞吐量。此问题可能有很多原因,例如源数据库更改频率或网络节流频率的意外增加。您可以通过准备其他资源,对所有相关组件进行动态纵向扩容或横向扩容,从而为这种情况做好准备。
  • 数据库不稳定。源数据库或目标数据库可能会出现稳定性问题,这可能会减慢数据迁移过程,或间断性地阻止访问。数据迁移过程可能需要频繁恢复。在这种情况下,故意进行的高可用性或灾难恢复切换可能会解决问题。切换会更改非功能性环境(机器和存储),可能有助于缓解问题。在这种情况下,您需要测试切换和数据库迁移恢复过程,以确保切换不会导致目标数据库中的数据不一致。
  • 事务日志文件大小耗尽。在某些情况下,事务日志存储在大小存在上限的文件中。可能会达到此上限,而数据库迁移便会因此失败。请务必了解数据库系统的哪些部分可以动态重新配置,以便在资源限制出现时予以解决。如果无法动态配置某些方面,则必须仔细确定初始大小。

您预先完成的测试越实际且全面,就越有可能发现潜在问题并提前解决。

后续步骤