不停机数据库迁移
我已经进行了一段时间的持续交付讨论 ,在我的讨论中,我描述了一种如何在不停机的情况下安全地将一个数据库迁移到另一个数据库的模式。 由于许多人联系我并要求提供更多详细信息,因此,我将在此处按承诺提供更多详细信息。
您可以使用此模式在两个不同的数据库之间迁移,例如在MySql和MongoDB之间或同一数据库中的两个模式之间迁移。
这种模式的想法是使用功能切换来进行懒惰的数据库迁移,以控制应用程序的行为并逐步完成迁移的各个阶段。
假设您要从“旧”数据库迁移到“新”数据库的两个数据库。
第1步
构建“新”数据库架构并将其部署到生产环境中。 在此阶段,您的系统保持不变,除了部署了一个新数据库(准备就绪后可以开始使用)之外,没有任何改变。
第2步
在您的应用程序中添加一个新的DAO,该DAO将写入“新”数据库。 您可能需要重构应用程序,以使其具有访问数据库的单个(或很少)点。 在您访问数据库或DAO的点上,您添加了一个多状态功能开关,该开关将控制写入数据库的流程。 此功能切换的第一个状态是“使用旧数据库”。 在这种状态下,您的代码将忽略“新”数据库,仅照常使用“旧”数据库。
第三步
开始写入“新”数据库,但将“旧”数据库用作主要数据库。 我们现在进入分布式事务世界,因为您永远无法百分百确定写入2个数据库可以同时成功执行失败操作。 当您的代码执行写操作时,它将首先写入“旧”数据库,如果成功,则还将写入“新”数据库。 请注意,在此步骤中,“旧”数据库处于一致状态,而“新”数据库则可能不一致,因为在“旧”数据库写成功的情况下,对它的写入可能会失败。
在进行下一步之前,让此步骤运行一段时间(数天甚至数周)非常重要。 这将使您确信新代码的写入路径可以按预期工作,并且“新”数据库已正确配置且所有复制都已到位。
任何时候,只要您确定某件事不起作用,您就可以简单地将功能切换回先前的状态,并停止写入“新”数据库。 您可以对新模式进行修改,如果需要,甚至可以删除它,因为所有数据仍在“旧”数据库中并且处于一致状态。
第4步
启用读取路径。 更改功能切换以启用从两个数据库中的读取。 在这一步中,重要的是要记住“旧”数据库是一致的数据库,仍应视为权威数据。
由于有许多读取模式,因此在这里我只介绍几个,但您可以根据自己的用例进行调整。
如果您的数据不可变,并且您知道首先从“新”数据库中读取的记录ID,并且如果您找不到该记录,则需要退回到“旧”数据库并在此处查找记录。 仅当两个数据库都没有记录时,您才向客户端返回“未找到”。 否则,如果找到记录,则返回结果,而不是“ new”数据库。
如果数据是可变的,则仅当时间戳等于“旧”数据库中的记录时,才需要从两个数据库中执行读取操作,并且更喜欢“新”数据库。 请记住,在此阶段中,只有“旧”数据库被认为是一致的。
如果您不知道记录ID,并且需要获取未知数量的记录,则基本上需要查询两个数据库并合并来自两个DB的结果。
无论您采用哪种读取模式,请记住,在这种情况下,一致的数据库是“旧”数据库,但是在此阶段,您需要尽可能多地读取和使用“新”数据库读取路径,以便测试您的应用程序和您在实际生产环境中的新DAO。 在此阶段,您可能会发现缺少某些索引或需要更多的只读副本。
在进入下一个阶段之前,让该阶段运行一段时间。 像在上一个阶段中一样,您始终可以将功能切换回原来的状态,而不必担心数据丢失。
需要注意的另一件事是,由于您正在从两个模式中读取数据,因此可能需要维护这两个数据集的向后和向前兼容性。
第5步
使“新”数据库成为主要数据库。 将功能开关更改为首先写入新数据库(您仍然可以从两者中读取,但现在更喜欢新数据库)。 这是非常重要的一步。 在这一步中,您已经运行了一段时间的代码读写路径,当您感到舒适时,现在就可以切换角色并将“新”数据库设为一致的数据库,而将“旧”数据库设为不一致的数据库。 现在,您无需首先写入“新”数据库,而是先写入“新”数据库,然后尽最大努力写入旧数据库。 此阶段还要求您更改读取优先级。 到目前为止,我们认为“旧”数据库具有权威数据,但是现在您希望使用“新”数据库中的数据(当然,您需要考虑记录时间戳)。
这也是您应尽力避免将功能切换切换回先前状态的要点,因为您将需要运行手动迁移脚本来比较两个数据库,因为它们可能会写入“旧”数据库未成功(记住分布式事务)。 我称此为“ 不归路 ”。
第6步
停止写入“旧”数据库(从两者读取)。 再次更改功能切换,现在停止写入只有“新”数据库的写入路径的“旧”数据库。 由于“新”数据库仍然没有所有记录,因此您仍然需要从“旧”数据库以及新数据库中读取并合并来自两者的数据。
这是重要的一步,因为它基本上将“旧”数据库转换为“只读”数据库。
如果您感觉足够舒适,则可以一次性执行步骤5和6。
步骤7
将数据从“旧”数据库迁移到“新”数据库。 现在,“旧”数据库处于“只读”模式,非常容易编写迁移脚本,以迁移“旧”数据库中不存在于“新”数据库中的所有记录。
步骤8
删除“旧” DAO。 这是将所有数据迁移到“新”数据库的最后一步,您现在可以安全地从代码中删除旧的DAO,而仅保留使用新数据库的新DAO。 您现在当然应该停止从“旧”数据库中读取数据,并删除处理来自两个DAO的合并数据的数据合并代码。
完成此操作后,就可以安全地在两个数据库之间迁移数据,而无需停机。
旁注:在Wix中,我们通常分别执行第3步和第4步至少2周,有时甚至一个月,然后再进行下一步。 在这些步骤中遇到的问题的示例是:
在写路径上,我们将大型对象保存在内存中,这在高峰流量期间引起了GC风暴。 复制配置不正确/无法正常工作。 缺少适当的监视。
在读取路径上,我们遇到诸如缺少索引的问题。 效率低下的数据模型导致性能不佳,这让我们重新考虑了数据模型以获得更好的读取性能。
翻译自: https://www.javacodegeeks.com/2015/12/safe-database-migration-pattern-without-downtime.html
不停机数据库迁移