Nestjs框架: 数据库的多种选择,与单库和多库的分析

数据库模块集成之ORM选择


1 )概述

  • NestJS 本身和数据库并无直接关联,它的底层使用 Express 或者 Fastify 来提供 HTTP 接口服务
  • 在数据库方面,NestJS 使用 Node.js 端的一些驱动,也就是 NestJS 端的数据库驱动来与数据库进行对接。
  • 不过,它更推荐使用一些 ORM(对象关系映射)库来与数据库对接
  • 主要原因在于,这些 ORM 库能够与各式各样的数据库进行对接,做到“开箱即用”(out of the box)
  • 我们无需根据不同的驱动或者不同的数据库去定制不同的逻辑代码
  • 官方指出,市面上有不少值得推荐的 ORM 库,像 MikroORM、Sequelize、Knex.js、TypeORM 和 Prisma 等
  • 同时,官方还推出了几个中间件库,如 @nestjs/typeorm、@nestjs/mongoose 等
  • 此时,我们就需要思考到底选用哪一款 ORM 库,哪一款在后续开发中能为我们节省大量工作

2 ) ORM 库的选择

  • NestJS 官方不推荐直接使用 Node.js 端的驱动,而是推荐使用 ORM 库,主要目的就是这些 ORM 库可以实现一个 ORM 对接多个数据库

  • 就拿官方推荐的 TypeORM 来说,打开 TypeORM 官方网站,在左侧菜单有个“DataSource Options”,其中关于“type”的部分,明确写着 TypeORM 可以与多种类型的数据库对接,常见的有 MySQL、PostgreSQL、SQLite、MariaDB、Oracle 等,甚至还包括 MongoDB 等

  • 另外,Prisma 也是非常推荐的,它同样能与各式各样的数据库对接。在 Prisma 的官方网站上,对于它支持的数据库类型和版本都有详细描述,包括 MongoDB、PostgreSQL、MySQL 以及 SQL Server 等,这些都是很常见的数据库

  • 不同的 ORM 库之间是存在区别和侧重的,所以在选择时,有几个要点需要考虑,虽然 ORM 库是更高层面的抽象和封装,能让一套代码操作不同的数据库,可针对不同用户的应用场景进行数据库对接,比如有的用户购买了商业化授权的 Oracle 数据库,有的使用付费版的 PostgreSQL、MySQL,还有的使用免费的 MySQL,这些情况都有可能

  • 因此,我们选择时要兼容最多的场景,同时还要考虑该 ORM 库在兼容这些场景后的稳定性、后续扩展性以及社区支持情况

  • 这和我们平时挑选 NPM 上的第三方包的策略很相似,大家可以去相关 ORM 库的官方网站看看,也可以参考 NPM 上的下载量

  • 很多 ORM库,比如:MicroORM 这类,像 Sequelize、TypeORM、Prisma,Mongoose

  • 虽然严格来说,Mongoose 不算传统意义上的 ORM 库,但它是与 MongoDB 对接的优秀驱动

  • 相比之下,Mongoose 在与 MongoDB 对接时表现出色。目前,TypeORM 仅支持 MongoDB 的部分版本,Prisma 支持 MongoDB 4.2 及以上版本

  • 从官方给出的表格能看到,对于 MongoDB 版本要求是 4.2 以上。对于一些老系统,若使用的是 MongoDB 4.0 版本,Prisma 就无法支持,此时就必须使用 Mongoose,而且 Mongoose 支持的操作方法更全面

  • 以 Prisma 官方介绍的“database features matrix”为例,它支持事务、索引等常见属性,但也有一些不支持的功能,比如聚合管道(aggregation pipeline),这在编写复杂的 MongoDB 查询时非常重要,Prisma 不支持这一点比较可惜

  • 我们试图在 NestJS 官方推荐的这些 ORM 库中找到一个万能的 ORM 库,但目前来看,TypeORM 虽比较接近我们的使用场景,但在操作 MongoDB 方面,还是不如 Mongoose

  • 不过,这两者对常见的关系型数据库支持都很好。在 TypeORM 官方网站搜索 MongoDB 会发现,它虽支持 MongoDB,但指定的是特定版本(experimental),而现在 MongoDB 已经更新到 7.x 版本,如果只能安装 5.x 版本的驱动,就无法对接 6.x、7.x 版本

  • 有的同学可能想把 TypeORM 和较新的 MongoDB 版本集成,但搜索相关 issues 会发现有很多坑点,所以不建议这么做

  • 如果使用 Mongoose 对接 MongoDB,就意味着需要维护两套代码,一套是 Mongoose 对应 MongoDB 的 Schema,另一套是 TypeORM 或 Prisma 的实体类,这就需要我们进行权衡考量

  • 权衡点主要有:一是是否需要对接 MongoDB 数据库;二是对接的 MongoDB 数据库版本是多少;三是如果对接 MongoDB 数据库,是否需要使用其进阶特性。

  • 如果不需要对接 MongoDB 数据库,或者只是可能会对接但不使用其进阶特性,那么可以考虑使用 Prisma,因为它能涵盖市面上绝大多数应用场景的数据库;如果觉得 Prisma 涵盖的场景不够,也可以考虑 TypeORM,但这会带来权限问题,因为它们定义数据库连接和使用的方式不同,可能需要在 ORM 库与数据库操作层面或逻辑层面写一层新的封装,封装常见的数据库操作方法,如根据 ID 查询、查询所有、删除、更新等

  • 一般来说,我们的业务系统大多只对接一种类型的数据库,要么是关系型数据库,要么最多扩展到 Redis 这种非关系型数据库。不过,也有一些特殊场景可能需要对接多种数据库,比如跨系统升级时,需要从老旧系统读取数据;还有低代码类型项目,需要让用户使用自己本地不同版本的数据库,这时就需要 ORM 库的支持

  • 后续我们会将 Prisma、TypeORM 以及 Mongoose 集成到我们自己的项目中,但在业务代码层面,不会同时使用这三者,主要有以下三点原因

    • 第一,同时支持这三者代码量会非常大,因为 Prisma 需要定义 Schema,TypeORM 需要定义实体类,Mongoose 需要针对 MongoDB 设计对应的 Schema,且它们之间并不通用
    • 第二,Prisma、Mongoose 和 TypeORM 官方文档都比较全面,我们后续的业务会基于其中一个方案进行扩展,如果需要扩展到其他方案,可以参考官方文档
    • 第三,这三者支持的数据库各有侧重,在通用模块项目中,我们虽要考虑支撑市面上所有数据库类型,但不能本末倒置,要考虑投入产出比,因为我们的使用场景大多只对接一种数据库,即便考虑分布式数据库,也可能是同一版本的数据库
  • 当我们既需要将数据存到关系型数据库,又需要存到像 MongoDB 这样的非关系型数据库时,官方文档给出了示例

  • 在官方的 database 部分有一个“multiple database”示例,我们既可以引入 TypeORM,也可以扩展 Prisma 或者对接 MongoDB

  • 这样一来,同一个项目中可能既存在 MongoDB 的 Schema,也存在 Prisma 的 Schema 和 TypeORM 的实体类,组织代码时就需要格外慎重,以免混淆

  • 在 NestJS 里连接数据库的方式和形式非常丰富,我们既可以在一个 NestJS 应用里对接多个数据库,甚至是多个不同类型的数据库

  • 这就是我们关于在通用模块项目中集成数据库模块的建议,我们后续对接数据库时,要明确自己的目标,项目是为了支撑后续业务开发,而不是纠结于覆盖所有数据库类型

NestJS 和 ORM 之间的关系

  • ORM 库是 NestJS 与数据库之间的桥梁,这里挑选了三个相对典型的 ORM 库
  • 对于 TypeORM 方案来说
    • 它支持的关系型数据库非常丰富,但对非关系型数据库的支持不太友好
  • 对于 Prisma 方案来说
    • 而 Prisma 操作数据库层面很方便,不过对关系型数据库的支持不够丰富
  • 对于 Mongoose 方案来说
    • 对非关系型数据库支持得非常好

在 NestJS 中应选择的哪种方案


  • 如果不谈业务只谈技术,那就是耍流氓。就像乔布斯所说,做产品要从业务需求出发
  • 所以在生产过程中,大家在这三者中做选择时,不用过于纠结
  • 因为根据自身业务场景,答案可能就已经明确了
  • 比如,若只是使用开源的 MySQL 方案,加上 TypeORM 或许就能解决问题
  • 若对 MySQL 没有版本需求,团队开发时对数据库操作层面没有精细化要求
  • 且需要方便的操作,那么 Prisma 就是首选

1 )TypeORM

  • 如果团队需要对接多种关系型数据库,其中还有客户指定的特殊数据库,或者有较旧的 MySQL 版本需要对接,那么毫无疑问应选择 TypeORM
    • 因为它对关系型数据库的支持非常全面,即使是较低版本也能很好地支持,其官方文档全面,生态也最完善
    • 在处理一对多、多对多关系,或是操作关键表时,可能会纠结关联字段和 ID 该放在哪一边,TypeORM 都有完善的文档可参考
    • 而且,若不想使用它的官方方法,还能使用原生 SQL 语句操作数据库,从这些方面来看,它无疑是 ORM 库中的“老大哥”

2 ) Prisma

  • Prisma 主要针对习惯写 TS 的小伙伴,它简化了数据库层面的操作,开发了 Prisma Client, 其中封装了数据库操作
  • 理解这些关键细节后,再看 TypeORM 以及 Prisma 官方对它们的介绍和代码,结合业务目的,就不会那么纠结了

3 )单库 和 多库的选择

  • 如果每天的数据量不大,在十万以内,选择单库方式就可以,那什么情况下选择多库,什么情况下对接多种数据库呢?
  • 有时候,对一种数据库(如 MySQL)进行读写分离,设置主从数据库即可,通常写操作是数据库的瓶颈,读操作则不是
  • NestJS 能很好地支持对接一种数据库的多库,带着这样的问题去看 NestJS 的官方文档,就不会对代码和技术方案感到陌生和堵心了
  • NestJS 是一个大而全的框架,涵盖了所有服务端开发的应用场景,大家可以针对自身业务在其生态中找到解决方案
  • 做技术的小伙伴要记住,学习和使用技术是为业务服务的,我们选择技术方案是对技术复杂度提升后应对的场景
  • 多库场景,可以定义多个数据库并命名,后续操作时用依赖注入的方式选择操作哪个数据库,例如,一个数据库用于写操作,另一个用于读操作
  • 读写分离通常能解决性能问题,主数据库一般用于写,从数据库用于读,数据库之间的同步问题通常可由数据库层面解决。若网络状况良好,同步问题基本能得到解决
  • 数据库集群一般依靠外部工具(如 K8s),通过容器化方式快速启动多个数据库实例,再加上数据库之间的同步和负载均衡,就能承载较大的业务系统

单库和多库优缺点

  • 在具体业务场景中,可针对某一种 ORM 库做具体选择,绝大多数场景是单库单 ORM
  • 下面分析下单库和多库的优缺点和应用场景。

单库

  • 优点:适用于对成本敏感、数据承载量不大、安全性要求不高的场景,成本较低。常用于小程序、公众号等需要快速开发和部署的应用场景
  • 缺点:可能受物理机硬件限制,导致性能和扩展性受限。若出现故障,存在数据丢失风险

多库

  • 应用场景:

    • 对自定义需求非常高,不同用户要求使用不同数据库(如 MySQL、MongoDB、Oracle 或 SQL Server)。
    • 一种数据库多库,可考虑用一个 ORM 对接
    • 大型业务场景,或对合规和法规要求严格,必须进行物理隔离时,需用多个 ORM 对接多种数据库
  • 优点:能满足复杂业务需求,不同类型数据库发挥各自优势。

  • 缺点:架构变得复杂,需了解多种数据库的操作、部署和维护方式

    • 有些数据库在本地
    • 有些在用户侧,尤其是低代码项目和数据敏感项目
    • 此外,还可能出现数据一致性问题,分布式读写需要加入事务处理,这较为复杂,且不同数据库对事务的支持情况不同
    • 如 MongoDB 在 4 版本之后才支持事务,老旧项目使用旧版本时需考虑数据库升级和迁移

通用框架,如何考虑涵盖单库和多库这两种场景

  • 第一步了解 NestJS 如何与这三个 ORM 库做基础对接, 第二步再深入探讨如何将它们融合到框架中
  • 以业务场景为例,探讨如下:

单、多库的选择

  • 如日记系统需分成多个租户,每个租户用单独字段做逻辑隔离,安全性要求不高、数据量适中,可使用单库场景
  • 若用户安全性要求高,需将素材采集的图片数据存放到自己的数据库,就采用多库场景

关系、非关系型 数据库选择

  • 通常,文件操作对非关系型数据库(如 MongoDB)友好,素材设备采集的动态数据也适合用非关系型数据库存储
  • 而用户管理系统等内容用关系型数据库更好
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值