在此博客文章中,我们介绍如何使用Bucardo将Postgres数据库迁移到具有零停机的新实例。我们将介绍如何避免像数据丢失,恶化性能和数据完整性失败等常见陷阱。我们已成功使用此过程将我们的Postgres数据库从9.5版本迁移到RDS上,但该过程仅限于RDS,并且不依赖于特定的任何AWS。任何自托管或管理的Postgres都应该进行这种迁移策略。
在本文中,我们将讨论从一个数据库到另一个数据库中迁移多个Web应用程序,如微服务。现代架构包括多个应用程序(或如果您喜欢的微服务),而每个应用程序都有多个运行实例以方便缩放。为了能够将应用程序移动到新数据库,您必须首先确保两个数据库中的数据同步,并且在任何给定的时间点保持如此,否则您的客户将最终失去数据,甚至最终最终有无效状态。
一个天真的解决方案是为了停止写入旧数据库,拍摄快照,将其恢复到新数据库,并在那里恢复操作。这种情况包括一个不可接受的停机量,不适合生产环境。我们只提到这一点是为了教育目的,因为它是确保您不会丢失任何数据的最简单方法,但您可能会丢失一些客户。
一种更现实的方法是在两个数据库之间建立一个近乎实时的双向复制,因此,理想情况下,应用程序可以从并写入两者而不明显任何差异。此方法将允许您逐个将应用程序逐步移动,一个实例一次,无需停机,而不会影响您的用户。由于我们希望我们的应用程序能够写入两个数据库,因此我们需要在多主机复制后进行。谷歌搜索“Postgres中的多主复制”将向您提供大量解决方案,每个解决方案都有几个优点和缺点。我们决定继续进行Bucardo,因为它是开源,快速,并提供简单的监控和冲突解决机制。
Bucardo作为两个Postgres实例之间的中间人。只要它可以到达源和目标数据库,您就可以在您喜欢的机器上运行Bucardo。安装它并设置多主复制后,Bucardo将为您选择复制的所有表添加一些额外的触发器。运行Bucardo的实例在本地使用单独的PostgreSQL数据库以保存同步状态。这样,您可以暂停并重新启动同步。发生更改时,触发器将为Bucardo实例的Postgres中的“Delta”表添加所有受影响的主键,另一个触发器将“启动”同步。每次同步被踢,Bucardo都会将每个表的受影响的行进行比较,以为所有主人选择赢家,然后它将与其其余数据库的更改同步。挑选胜利者并不简单,此时可能发生冲突。 (我们分析了不同部分中的冲突。)
几个指南在线表明,使用bucardo的正确方法是拍摄源数据库的快照,将其恢复到一个新的,然后启动多主站bucardo同步。不要这样做!如果您这样做,您即将获得与当前数据库大小和写流量成比例的数据丢失。这是因为拍摄快照并恢复它需要大量的时间。在此期间,源数据库将在发生写入时开始漂移,并且必须同步此漂移,以确保两个主机包含相同的数据。这里的抓住是人们认为Bucardo会做一些背包,但事实证明,这项任务是不可靠的,可能无法同步大漂移。您可以始终使用将数据库跨数据库进行比较的工具,以确保已消除漂移,但如果数据集是巨大的,则会浪费大量时间,如果零停机是我们所在的寻找。此外,有足够的复制滞后,您最终可能会被检测为假正漂移的飞行同步。
您可以使用autokick = 0标志启动Bucardo Sync并告诉它以缓存其本地数据库中的所有漂移。不幸的是,这个选项至关重要,但没有记录!这一步骤至关重要,我们的知识唯一明确的参考由David E. Wheeler的优秀博客文章中发生。
注意autokick = 0。这可确保在记录Deltas时,在我们告诉Bucardo这样做之前,他们将不会被复制。
使用此标志的实现是什么,您可以在本地可以在Bucardo实例中缓存Delta,直到您有足够的时间准备新数据库。这是至关重要的,特别是对于大漂移。
这里有两个选择。您可以从您的第一个数据库中获取全包快照并将其恢复为新实例,或者您可以从新的空数据库开始,传输用户,架构,然后转移数据 - 以该顺序分别 - 单独。我们建议后一种方法。原因是在基准测试后,第二个是清洁剂。我们有机会从头开始撤消旧用户帐户,临时表和细粒度。
如果您使用AWS RDS,所提出的解决方案也更快。拍摄快照可以花费几分钟,具体取决于您的数据库大小。此外,如果要从未加密的服务器迁移到使用加密 - 休息的服务器,如我们所做的那样,您需要拍摄快照,加密快照,然后将其还原到新的RDS实例。这是更耗时的方式,最小的迁移时间是我们的主要目标。
在开始Bucardo同步之前,您需要正确配置。您需要指定两个数据库,它们的类型(Master /副本)以及数据库的哪些部分应包含在同步中。您可以批量添加架构中的所有表,这对具有大量表的数据库非常有帮助。
Bucardo无法同步没有主键(PK)的表,该表完全预期,因为它无法区分唯一条目。我们不得不在过程中排除一些表,该表充当各种表迁移的缓存,并且不包含PK。一些未使用的表也被排除在外,因此我们没有将未使用的数据转移到我们的新数据库。 Bucardo轻松支持此项:添加所有表后,您可以删除要排除的表。
Bucardo不会迁移Postgres用户。您需要手动传输您的用户帐户。我们为此目的写了一个脚本,您可以在这里找到。该脚本进入新数据库,从配置服务器检索的密码创建新用户,然后设置其权限。虽然您可能不会以代码保留您的数据存储,但它是一个很好的做法,以便在灾难中可以恢复它们。
您可以使用Postgres及其PG_DUMP / PG_RESTORE工具来传输您的模式和数据。这是一个简单的一步,捕获。请记住,此时我们已经有Bucardo启动并运行录制漂移,因此将在目标服务器上恢复数据将被解释为要同步到源数据库的更改。这就是通过启用Session_Replication_Role =副本标志,我们需要使用副本会话将数据还原到目标Postgres数据库上的原因。我们需要在启动连续同步之前禁用它。
高可用性,零停机迁移的先决条件通常是每个应用程序都有多于一个运行实例的要求。通常每个每个人都应该在重新启动之前排出,因此无法在完全相同的时间点将所有实例切换到新数据库 - 如果无论如何都有这样的事情。因此,将始终存在关键 - 或长时间窗口,其中相同的应用程序将写入两个数据库,并且在此期间可能发生冲突时。
冲突很少见,因为他们需要在Bucardo可以复制两种记录之前在两个数据库中进行两次写入。通过近零复制时间,您根本不会有任何冲突,但这种迁移正在发生在关键的生产环境中,因此他们不能被忽略。
想象一下,两位客户试图为同一日期预订同一个房子。如果他们都尝试在完全相同的时间内完成,并且每个都指向不同的数据库,则可能发生冲突。 Bucardo附带一个冲突解决机制,为您提供了两个基本选项:让Bucardo处理冲突(默认选项),或中止同步并手动解决它们。这是迁移过程中最关键的部分,因此让我们进一步分析。
如果您的表具有自动递增的ID作为主键,Postgres将自动从相应的序列中选择下一个ID。 Bucardo同步序列也是如此。假设在上面的例子中,您将具有自动递增ID作为PK的预订表,并且最新的录制ID为42.并发插入发生,两个数据库中创建了两个不同的记录,两个数据库都是一个pk但是不同的数据。如果让Bucardo处理冲突,它将只保留最新的一个并删除另一个。您最终会丢失遗失的预订,这些预订对于您的客户来说也是成功的。您的数据库将处于有效状态,但您将丢失无法恢复的数据。这是一个死胡同!
在跳入解决方案之前,让我们考虑另一种情况。假设您的表格使用UUID作为PK。使用并发预订重播上述方案将创建两个不同的记录,两个数据库中有两个不同的PKS。这次没有发生冲突。 Bucardo将成功同步两个数据库中的记录,但您的数据仍然无法从业务角度无效,因为您无法预订同一所房子两次。因此,目前可以清楚地清楚,数据库有效性并不保证您从业务角度上有效的数据。您需要小心您处理冲突,因此您的客户不会在道路上遇到问题。
Bucardo支持自定义分辨率策略。您可以根据您的业务需求编程自己的策略,但这可以很快得到太复杂和太耗时。另一种方法是创建自己的工具来检测和解决迁移期间的数据违规。这不是一项简单的任务:必须根据数据的复杂性设计,可能需要太多的开发工作。
我们的解决方案是通过在开始迁移之前实现两个条件,确保冲突根本不会发生。首先,我们努力最小化数据库之间的过渡时间,以最大限度地减少冲突概率。这是通过跨对应用程序的重新配置来指向新数据库,一次实例来实现这一点,但是对于并行的所有不同应用程序。第二个和最重要的步骤是,就在我们开始将应用程序切换到新数据库之前,我们从旧数据库中的App用户撤消了写入权限。这样,我们可以确保冲突是不可能的,在少量时间内的少量时间下的权衡将失败。这 - 当然 - 要求您的应用程序优雅地处理失败的数据库写入。您的应用程序应独立于任何数据库迁移执行此操作,因为这对生产环境至关重要。
在本节中,我们将介绍我们遵循的步骤,并且脚本每个其中一个对应于。我们已将代码上载到此GitHub存储库,我们将分解为下面的碎片。
旋转新实例(在我们的情况下EC2)。该指令将假设您运行Debian OS。
(可选)如果您之前使用过源数据库中的Bucardo,则可能需要通过运行uninstall_bucdo.sh来清理旧触发器。在您运行之前,请查看我们基于我们的数据库生成的uninstall.template。您需要在那里列出所有表格。
您需要手动运行$ Bucardo安装以完成本地Bucardo安装。
仔细查看Configure.sh脚本。在这里,您需要编辑脚本以匹配迁移方案。您需要为Bucardo对象定义描述性名称,并指定您的排除表或省略此选项。在您了解此脚本所做的操作后,您可以继续运行它。脚本执行以下操作:设置.pgpass文件和bucardo alias命令,以避免在进程中询问密码的交互式提示中断
配置Bucardo数据库,群体,数据库组和同步。如果您需要更熟悉Bucardo对象类型,则文档页面中有一个列表。
在新PostgreSQL主机中初始化一个空数据库,并通过运行此脚本创建用户。您需要编辑脚本以在此指定您的角色。我们早先源的vars.sh文件检索密码。
以压缩格式传输数据库数据。虽然数据被传输并漂移开始堆积,但是Bucardo将在本地保存并重新扮演一旦Autokick标志改变值
重置Autokick标志的值,以便将本地缓存停止,然后重新加载配置,以便同步尊重新值
现在连续同步就是就地,是时候开始在新数据库中移动应用程序了。对我们来说,这是通过在配置服务器中更改应用程序参数然后将其重新部署一个逐一的应用程序来完成。在此步骤中,我们需要在旧数据库中使用户的权限。一旦我们的应用程序连接到新数据库,我们通过运行Revoke_Write_Access_from_old_db.sql脚本更改了旧版本中的权限。定时在这一点上至关重要。
虽然您有同步运行,但应验证数据复制。我们通过使用叉子PGDataDiff工具来完成它。我们还通过允许数据差异排除表来扩展它。
将所有应用切换到新数据库后,您可以停止Bucardo同步并解雇其计算机。您应该再次运行uninstall_bucardo.sh,以便从触发器清理新数据库。
将PostgreSQL数据库迁移到新实例具有巨大的挑战。无论你选择这样做的工具,你将面临的挑战保持不变:
在本文中,我们描述了我们如何解决它们。我们缺少的主要困难之一是在线缺乏同类教程,因此我们必须多次通过我们的解决方案即兴创作和迭代,直到我们正确地完成。您需要您的反馈来改进过程,并使其对可能面临同样问题的其他人有价值。
2020年初发现我们使用了从蓝色初期以来我们使用过的两篇文章9.5实例。 2020年1月,我们不得不解雇旧实例并使用新的实例,因为亚马逊即将迁移到新的SSL / TLS证书。这种迁移最终有了可收回的数据丢失,这为我们花了几个艰难的日子来解决。出错的是,我们信任Bucardo自动同步我们的漂移,如上所述 - 是错误的,失败了。今年,我们不得不再次这样做,因为Postgres 9.5 EOL日期,否则他们将被AWS强行升级。这次我们决心关注每一个细节。我们相信我们可以快速,可靠地和失败安全,我们做到了。
首先,我们需要解释为什么我们没有让亚马逊在飞行中升级我们的数据库,而无需我们根本。亚马逊提供升级过程,但与迁移到新数据库实例相比,它有一些严重的缺点:
AWS RDS不向您提供即时回滚选项。在迁移过程中有两个实例,回滚将是我们应用程序的简单重新配置,以指向旧数据库。这是整个过程中的巨大故障安全措施。
透明度。如果RDS未能升级数据库,则有延迟或性能问题,我们根本无法执行任何行动。在生产环境中,您需要有一个坚实的回滚计划,以防任何南方。
我们想要的一些功能在当前的情况下,如加密 - 休息和RDS Insights。
我们与Bucardo一起去,因为我们希望在我们的VPC中解决一个Sandboxed,因此生产数据永远不会在互联网上暴露。结果是一个成功的迁移,没有丢失数据的风险。迁移过程的总持续时间不到2小时,被认为是相对成功!