MongoDB 到 Postgres

🚨 本博客文章是我的 MongoDB to Postgres (2024-03-06)Sequelize vs. Prisma (2023-05-25) 的合并。原始博客已被删除,此博客取而代之,因为两者基本包含相同的内容/信息。迁移于 2023年3月初 开始,2023年11月中旬 完成切换,所有旧的 MongoDB 系统实例在 2024年1月初 完全关闭。

介绍

在我于 eBay 工作期间,我遇到了我职业生涯中技术上最具挑战性的问题:将存储管理系统(STMS)从 MongoDB 迁移到 Postgres。这不仅仅是一次简单的数据库替换;它是对一个关键系统的完整架构转型,该系统每分钟在 eBay 的数据中心中摄取超过 150 万条指标,要求零停机时间并保持几乎所有现有功能。

STMS 是什么?

存储管理系统(STMS)是 eBay 的服务与存储基础设施(SSI)团队的关键内部工具。它监控并管理 eBay 数据中心中的设备,使工程师能够:

  • 监控来自数十个阵列、交换机、主机、磁盘组和集群的指标
  • 处理交换机和阵列的警报
  • 完成诸如主机分配等高级任务
  • 为其他内部 eBay 服务访问实时数据

STMS 涉及超过 70 台阵列、60 台交换机、1100 台主机、900 个磁盘组和 200 个集群,遍布 eBay 的 3 个数据中心。鉴于其在 eBay 基础设施中的重要角色,任何停机或功能丢失都会直接影响公司的核心服务和业务运营。

挑战

为什么需要迁移

从 MongoDB 迁移到 Postgres 的决定并非轻率。虽然 MongoDB 最初为 STMS 提供了良好支持,但我们数据关系的日益复杂以及对更高级查询能力的需求,使得 Postgres 成为我们用例的更长期解决方案。

使此问题困难的原因

此次迁移的复杂性源于多个根本性挑战:

1. 基本的数据库差异
MongoDB 和 Postgres 本质上是不同的数据库。MongoDB 是文档型数据库(NoSQL),数据以 JSON 形式存储在集合中,类似于文件柜中的文档。Postgres 是关系型数据库(SQL),数据以表格中的行形式存储,类似于电子表格。

2. 代码库架构
STMS 的整个后端是为处理和管理 JSON 数据而构建的,使用的包仅兼容 MongoDB 进行数据库操作。这意味着不仅要更换数据库,还要重构整个应用处理数据的方式。

3. 零停机要求
由于 STMS 作为内部工具的重要性,迁移期间不能有任何停机。系统必须在整个过程始终每分钟继续处理 150 万以上的指标。

4. 紧迫的时间表和有限的经验
迁移必须在几个月内完成,且最初没有明确的执行计划。我和同事们都没有将大型遗留代码库从 NoSQL 迁移到 SQL 数据库的经验,而我对 Postgres 的经验也相当有限。

5. 规模与复杂性
迁移涉及将 36 个 MongoDB 集合转换为 74 个 Postgres 表,需要仔细考虑关系、索引和查询优化。

选择合适的 ORM:Sequelize vs Prisma

在决定使用哪种 ORM(对象关系映射)工具时,这是首要的重大决策。由于我们的代码库已经设计为使用 Mongoose 操作 MongoDB,选择一个 ORM 能提供最平滑的过渡路径。

需求分析

在对项目需求进行仔细分析后,我为任何 ORM 解决方案设定了以下关键标准:

  • 必须是 JavaScript 包(我们的大部分代码都是用 JavaScript 编写的)
  • 必须支持 Postgres 及其大多数特性
  • 性能必须至少与 Mongoose 持平或更好
  • 必须是开源且有维护

候选者

经过广泛调研,我将候选范围缩小到两大选手:Sequelize 和 Prisma。我使用 Docker 为 Postgres 创建了完整的测试环境,并将我们最大、最复杂的数据集从文档结构转换为表结构进行测试。

测试方法

对每个 ORM,我测量了关键操作的性能:

  • 创建条目的时间
  • 更新条目的时间
  • 更新嵌套条目(关系和 JSON 键值)的时间
  • 删除条目的时间
  • 查询/获取条目的时间

决策:Sequelize

在 2023 年 5 月 15 日左右,我决定 Sequelize 更适合我们的使用场景。原因如下:

Sequelize 优势:

  • 真正的开源且不是由有资金的初创公司维护
  • 支持大多数 Postgres 的特性
  • 性能更佳,尤其相较于 Prisma
  • 生态成熟,已有超过 10 年的开发历史
  • 使用 JavaScript 类实现灵活的模型/模式表示
  • 支持复杂的连接和过滤选项,包括正则表达式

性能结果:

在我的测试中,Sequelize 显著优于 Prisma。对于我们的大型数据集条目:

  • Sequelize:约 2.26 秒/条目
  • Prisma:约 11.21 秒/条目

Prisma 的速度约为 Sequelize 的 5 倍。另有测试显示,从我们最大的数据集中删除单条记录时,Prisma 需要近 4 分钟,这对我们的需求来说是不可接受的。

Sequelize 挑战:

  • 模型表示更复杂且臃肿(564 行 vs Mongoose 的 262 行)
  • 某些情况下语法令人困惑
  • 数据库迁移复杂
  • 相较于 Prisma,文档不够全面

Sequelize 与 Prisma 的优缺点比较

为了更全面地说明我为何选择 Sequelize,我在评估期间为两者编写了详细的优缺点列表,并对比了它们在模式表示和社区支持方面的表现(截至 2023 年 5 月 15 日)。这次深入的比较帮助我坚定了选择,也希望对面临相似决策的其他人有所帮助。

Sequelize 优点:

  • 拥有 sync() 函数,可自动创建并处理表,省去大量手动工作。
  • 能处理嵌套数据的复杂连接,这对 STMS 的结构至关重要。
  • 支持广泛的过滤选项,包括正则表达式,提供查询灵活性。
  • 模型/模式表示使用原生 JavaScript 类,实现高度可定制。
  • 无缝处理数据库连接,包括对多个读取连接的支持。
  • 支持原始 SQL 查询,便于深入底层操作。
  • 社区数据(截至 2023 年 5 月 15 日):NPM 最近更新 14 天前,周下载量 1,505,835 次;GitHub 最近更新于昨天,Fork 4.2k,Stars 27.9k。已开源超过 10 年,采用 MIT 许可证,我相信它会保持这种状态。

Sequelize 缺点:

  • 模型/模式表示可能非常复杂且臃肿。例如,我们的大型数据集在 Mongoose 中约 262 行(含空格),而在 Sequelize 中膨胀至 564 行。
  • 在某些场景下语法令人困惑且复杂,导致开发效率下降。
  • 迁移或编辑数据库相当麻烦。即使使用 sequelize-cli 生成迁移脚本,仍然繁琐,这也是大多数 ORM 的共性痛点。
  • 文档不够完善,虽有改进,但仍有不足。幸运的是,ChatGPT 对 Sequelize 有深入了解,帮助填补了文档空白。
  • 相较于 Prisma,类型敏感度较低,可能在某些项目中导致问题。
  • TypeScript 支持有限,虽然这对 STMS 不是问题,但对其他项目可能是决定因素。

Prisma 优点:

  • 使用自定义的模式语言,使模型创建更简洁、更紧凑。对比之下,Mongoose 需要 262 行,而 Prisma 只用了 221 行。
  • 附带 CLI 工具,简化数据库创建和迁移,是我迄今为止见过的最好的 ORM 工具,尽管并非完美。
  • 支持原始 SQL 查询,在需要时提供灵活性。
  • 代码语法简洁,易于理解,相比 Sequelize 更易上手。
  • 通过其客户端自动生成 Node.js 和 TypeScript 的查询构建器,使用体验良好。
  • 文档优秀且整洁。虽然 ChatGPT 对 Prisma 的更新不如对 Sequelize 那么及时,但官方文档弥补了这一点。
  • 社区数据(截至 2023 年 5 月 15 日):NPM 最近更新 6 天前,周下载量 1,344,705 次;GitHub 最近更新 3 小时前,Fork 1.1k,Stars 31.3k。

Prisma 缺点:

  • 不支持 Postgres 的正则表达式过滤,虽然提供了 “contains”、 “includes” 和 “startsWith” 等替代方案。
  • 性能是我测试中的主要问题。对大型数据集创建条目时,Prisma 大约需要 11.21 秒/条目,而 Sequelize 只需 2.26 秒,约慢 5 倍。
  • 删除单条记录几乎需要 4 分钟,这对我们的需求是致命的。
  • 即使在复杂的三层深关系数据集上进行公平比较,Sequelize 在删除操作上仍显著更快。
  • Prisma 背后是一家获得 5650 万美元融资的初创公司。虽然其核心 ORM 代码在 Apache-2.0 下开源,但我担心未来可能出现许可变更,类似于 MongoDB 的情况。

这些详细的比较表明,Sequelize 在性能和长期可靠性方面更符合 STMS 的需求。但我把这些内容拆解出来,希望能帮助其他人在项目中面对相同选择时做出更明智的决定。

迁移过程

数据结构转换

将 MongoDB 的文档结构转换为 Postgres 的关系结构需要仔细的规划。我必须:

  1. 分析关系: 确定 MongoDB 文档之间的关联方式并设计适当的外键关系
  2. 规范化数据: 在适当的情况下将嵌套文档拆分为独立的表
  3. 保留 JSON 特性: 对于真正的非结构化且需要保持灵活性的数据,使用 JSONB 列
  4. 设计索引: 为查询性能创建合适的索引

定制解决方案

此次迁移需要开发多个定制解决方案:

1. 数据迁移脚本 我编写了完整的脚本以:

  • 从 MongoDB 集合中提取数据
  • 将文档结构转换为关系格式
  • 将数据导入具有正确关系的 Postgres 表

2. API 兼容层 为了保持零停机时间,我构建了一个能够:

  • 根据迁移状态将请求路由到 MongoDB 或 Postgres
  • 在过渡期间确保数据一致性
  • 提供回退机制

3. 定制中间件 开发了中间件来处理 MongoDB 与 Postgres 在某些操作上的差异,确保现有的 API 端点在不做修改的情况下继续工作。

克服技术挑战

处理复杂关系

最大的挑战之一是将 MongoDB 的嵌入式文档转换为 Postgres 的关系。例如,一个 MongoDB 文档可能包含:

  • 基本属性
  • 表示相关实体的嵌套对象
  • 嵌入式文档的数组

这需要被仔细拆分为:

  • 主要实体的主表
  • 多对多关系的关联表
  • 一对多关联的外键关系

查询优化

MongoDB 的查询模式不能直接转换为 SQL。我必须:

  • 将复杂的聚合管道重写为 SQL 联接
  • 为新的查询模式优化索引
  • 确保查询性能达到或超过 MongoDB 的性能

数据完整性

在迁移期间确保数据完整性需要:

  • 完整的验证脚本
  • 回滚流程
  • 过渡期间的实时数据同步

成果与影响

STMS 从 MongoDB 到 Postgres 的迁移在零停机时间内成功完成,同时保持了几乎所有的功能和特性。结果超出了预期:

性能提升:

  • 复杂关系查询的查询性能得到提升
  • 更好的数据一致性和完整性
  • 更高效的存储利用率

运营收益:

  • 增强的监控和调试能力
  • 更好地与 eBay 现有的基于 SQL 的工具集成
  • 改进的备份和恢复流程

团队影响:

  • 提升团队对关系数据库的认识
  • 为未来的数据库迁移建立了模式
  • 创建了可复用的工具和流程

获得的技术技能

该项目显著扩展了我的技术专长:

数据库技术:

  • 深入了解 Postgres 的特性和优化
  • SQL 查询优化与性能调优
  • 数据库设计模式与规范化
  • 主备数据库配置

开发工具:

  • Sequelize ORM 与查询构建
  • 数据库迁移策略
  • 性能测试方法论
  • 数据验证与完整性检查

架构模式:

  • 零停机迁移策略
  • API 兼容层
  • 数据库抽象模式
  • 监控与告警系统

个人与职业成长

此迁移项目对我的职业发展具有变革性。它将我推入未知领域,需要:

领导力技能:

  • 在没有先前经验的情况下领导复杂的技术项目
  • 在压力下做出关键的架构决策
  • 协调多个团队和利益相关者

问题解决能力:

  • 将复杂问题拆解为可管理的组件
  • 为前所未有的挑战开发创新解决方案
  • 平衡多个相互竞争的需求和约束

沟通与团队合作:

  • 向非技术利益相关者解释技术概念
  • 记录流程和决策以供将来参考
  • 指导团队成员学习新技术和模式

教训总结

技术教训

  1. 数据库选择很重要: 在 NoSQL 与 SQL 之间的选择应基于具体的使用场景和长期需求
  2. 性能测试至关重要: 理论优势并不总能转化为实际的性能提升
  3. 迁移规划: 对于复杂迁移,全面的规划和测试是必不可少的
  4. 工具投入: 预先构建合适的工具可节省大量时间并降低错误率

项目管理教训

  1. 利益相关者沟通: 定期更新和清晰的沟通可防止误解
  2. 风险管理: 拥有回退计划和回滚流程是必需的
  3. 时间线管理: 为意外挑战和学习曲线预留缓冲时间
  4. 文档编写: 完整的文档有助于知识转移和后续维护

结论

STMS 从 MongoDB 到 Postgres 的迁移是我职业生涯中解决的最具挑战性且最有价值的技术问题。它不仅需要技术专长,还需要领导力、规划和适应能力。项目的成功表明,只要有恰当的规划、彻底的测试以及对卓越的坚持,即使是最复杂的技术挑战也能被克服。

这段经历从根本上改变了我对软件工程的看法,强调了以下重要性:

  • 在做技术决策之前,先了解完整的背景和需求
  • 投入时间进行合适的工具建设和测试
  • 在整个复杂项目中保持清晰的沟通
  • 在必要时愿意学习新技术和新方法

迁移的成功不仅提升了 STMS 的能力,还建立了持续惠及 eBay 基础设施项目的模式和流程。它强化了我的信念:拥抱未知的挑战并通过它们取得成功是个人和职业发展的关键。

回顾过去,这个项目是我职业生涯的转折点,使我从一个实现解决方案的开发者转变为能够架构并领导复杂技术项目的工程师。从这段经历中获得的信心和技能仍在指导我应对软件工程中新挑战和机遇的方式。