从 SQL 迁移到 NoSQL 和主要重构
从 SQL 迁移到 NoSQL 和主要重构
介绍
随着我们的成长和业务需求的演变,一些以前的解决方案不再满足新的要求。有时我们成功地发展和改进我们的代码库以适应新的要求,而无需进行大的革命、重大的重构和复杂的迁移过程,但有时我们就是做不到。
在这篇文章中,我将讨论一个案例,我们不能仅仅改进我们的代码以适应新的需求,而是决定将我们的一项服务从 MS SQL 迁移到 MongoDB。
动机
我们的一项服务在 MS SQL 中有一个宽表,其中包含多个 JSON 列,即包含 JSON 数据的列。这是在不久前完成的,当时我们了解到我们需要实现一个动态的、部分非结构化的数据模型。当时,决定不使用其他数据库,因为不需要对 JSON 值进行搜索。随着时间的推移,我们遇到了这样的情况:我们有太多这样的列并且数据模型变得越来越动态。此外,随着时间的推移,我们开始过滤对这些 JSON 值的查询,这项任务给 MS SQL 服务器带来了很大压力。
与此同时,公司中越来越多的团队开始使用 MongoDB 作为他们选择的数据库。
我们还决定将此表迁移到 MongoDB 并利用基于文档的数据库的功能。
要求
从一个数据库迁移到另一个数据库是复杂、危险、容易出错且相当漫长的过程。
因此,我们必须设定一些严格的成功标准,以确保我们做对了:
1.正确性: 每个数据库中的数据表示不同,查询用不同的语言编写。我们在此重新设计中所做的更改可能会导致我们的数据库和/或 API 响应中的数据错误。
2.一致性: 要更改数据库,您需要将所有“旧”数据迁移到新存储,同时仍然服务于导致尚未迁移的新数据的新请求。
3. 零停机时间: 我们在后台所做的更改不应影响任何人。我们的 API 被组织内的其他服务所使用,这些服务期望响应保持快速和正确,而可能发
4、性能: 我们在新数据库中的不同查询和数据操作的性能可能与原始数据库中的不同。希望变得更好,但我们当然不能指望运气。
执行
我们决定分几个阶段执行迁移。每个阶段都可以交付和验证。
阶段1: 双写。
在这个阶段,我们做了以下工作:
1. 在 MongoDB 中创建具有所需索引的新集合。
2. 为 MongoDB 创建新的数据模型:
数据模型可能与 SQL 的数据模型非常相似,或者您可以借此机会对其进行更改。在我们的例子中,我们将现有的“ExtraDetails”JSON 属性扩展为 字典 <string, object> 财产与 [BsonExtraElements] 属性。此属性允许您在模型中拥有任何非结构化数据并将其映射到 JSON 作为子元素。这个“ExtraDetails”字段映射到一个 BLOB 列( NVARCHAR(MAX) ) 在 MSSQL 中,用于对象的任何非结构化数据。其他 JSON 字段也在新模型中表示为具有可能的“ExtraElements”字段的子实体。这使我们能够拥有更自然地用于 MongoDB 中的数据表示的数据模型,并且不需要对 JSON 属性进行序列化/反序列化数据。
创建一种将原始 SQL 数据库中的行映射到其等效的 Mongo 文档的方法非常重要。此外,两个模型都必须具有相互重新创建的选项。 MSSQL 通常使用一个数字 ( 整数 或者 长 ) 作为记录标识符,而 MongoDB 使用 对象标识 (一种 GUID,但更短)。我们需要在 MongoDB 模型中保存来自 SQL 的记录标识符,以便在我们仍在迁移过程中识别已经迁移的数据。在该过程结束时,可以删除旧属性。
3. 将服务对 MSSQL 执行的每个“插入”、“更新”或“删除”操作复制到 MongoDB 上。这可能是一项复杂而艰巨的任务,但具有良好的代码结构和使用存储库模式,它不仅可行而且不那么复杂。
所有 insert
命令都被翻译为 insert
到 MongoDB,所有 update
命令都被翻译为 upsert
(又名 Add 或 Update),所有 delete
命令都被翻译为 delete
,如果文档没有存在。
此阶段完全向后兼容。该服务仍然使用 MSSQL 作为主要数据源。
阶段2: 迁移现有数据。
前一阶段确保当我们生活在两个世界中(MSSQL 和 MongoDB 并存)时,所有更改都将反映到 MongoDB。但是在迁移过程中没有被更改的旧数据在 MongoDB 中仍然不存在。此阶段旨在将这些条目迁移到 MongoDB。
在这个过程中,我们手头有一个启用双重写入的特定时间戳,这让我们确信在 SQL DB 中所做的每一个更改也是在 Mongo 中完成的。所以现在我们只需要迁移在我们打开双重写入之前最后修改的每条记录。
迁移数据存在一些性能挑战:
- 大量写入可能会导致性能下降。另一方面,小批量将需要更长的时间。
- 读取还可能导致数据库上的额外负载,您可能需要考虑将这些卸载到副本。
在这个阶段结束时,我们应该让两个数据库具有完全相同的数据并且完全同步。
第三阶段: 双读。
在这个阶段,我们添加了从 MongoDB 读取的内容,同时仍然使用 SQL 作为服务主数据库。
此阶段的目的是确保两个数据库返回相同的结果。
我们使用了[ 科学家](https://github.com/scientistproject/Scientist.net)
包,它允许执行“实验”并比较它们的结果。
当然,也需要重写用于 MongoDB 的 SQL 的所有查询。简单的查询很容易,复杂的查询就很难了。
这个阶段也可能导致性能下降,因为我们正在查询两个数据库。确保它们被并行(异步)查询以减少降级。如果出现性能问题,您可以关闭 Mongo 的读取
第 4 阶段 : 从 MongoDb 切换读取。
这是一个简单但特别重要的阶段。我们所做的唯一更改是使用 MongoDB 作为读取的主数据库,使用 SQL 作为辅助数据库。在实践中,我们只是将 Try
和 Use
调用从 [ 科学家](https://github.com/scientistproject/Scientist.net)
包。
在此阶段结束时,您的系统可以使用新数据库,但您仍然可以切换回旧数据库,因为 SQL 仍然是同步的。
第 5 阶段: 删除旧代码、旧 SQL 表。
这是您删除旧代码和数据的最后一个阶段。这个阶段是不可逆的。在执行此阶段之前,请确保整个系统按预期工作。
结论
过去我们看到团队做出重大改变并以“大爆炸”策略部署它们,可能会出现什么问题?嗯,生产有它的方式让你感到惊讶,很多时候它最终会出现大量错误、不满意的同事和客户,以及一个致力于修补程序的团队。
使用一些简单的步骤,您可以顺利地实施实质性更改,而不会对消费者产生任何副作用。它可能更难实施,可能需要更多时间,但它避免了很多麻烦。
学分: 牵头的项目 尼尔·阿蒙 ,一起写的真棒 亚历克斯布罗特曼 这使得这一切都在生产中发挥作用。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明