2024年12月19日

关于 Prisma ORM 的五大误解

揭开关于 Prisma ORM 的五个常见误解的真相。在本文中,我们揭穿这些误解,探讨它们的起源,并进行一些有趣的区分事实和虚构的工作。

误解 1:Prisma ORM 速度慢

当我们最初在 2021 年发布用于生产环境的 Prisma ORM时,我们遵循了“让它工作、让它正确、让它快速”的方法。这意味着 Prisma ORM 的初始版本并没有特别针对速度进行优化。

但是,自那时以来,我们已在性能方面进行了大量投资,并且几乎在每个版本中都发布了性能改进。

我们还创建了开源的ORM 基准测试,比较了 TypeScript 生态系统中三个最流行的 ORM,并发现 Prisma ORM 的性能与其他 ORM 相似,有时甚至更快。

几乎每个版本都有重大性能改进

Prisma ORM 一直遵循每三周一个周期的稳定可靠的发布节奏。如果你查看prisma/prisma仓库的发布页面,你会注意到几乎每个版本都带来某种性能改进,例如对特定 SQL 查询的优化(如在5.11.05.9.05.7.05.4.05.2.05.1.0 等版本中可见),引入了新的批量查询,如createManyAndReturn(在5.14.0中),将冷启动速度提高了 9 倍(在5.0.0中)或引入了对基于本机 JS 的驱动程序的支持(在5.4.0中)。

我们还在努力将基于 Rust 的查询引擎从 Rust 重写为 TypeScript,以节省语言边界之间序列化的一些开销,并预计此更改也会带来显著的性能改进。

Prisma ORM 允许你选择最佳的 JOIN 策略

对于使用 Prisma ORM 的开发人员来说,另一个巨大的好处是,他们能够为其关系查询选择最佳的 JOIN 策略

原则上,当你需要从通过外键相关的多个表查询数据时,有两种不同的方法。

数据库级别:在单个查询中使用 JOIN 关键字

使用这种方法,你可以使用 SQL JOIN 关键字向数据库发送单个查询,并让数据库直接连接数据。

应用程序级别:发送多个查询并在应用程序中连接

当在应用程序级别连接时,你会向数据库的各个表发送多个查询,并在你的应用程序中自己连接数据。

何时使用哪种?

根据你的用例、数据集、架构和其他几个因素,一种策略可能比另一种策略更具性能。应用程序级别的连接方法也称为连接分解,通常用于高性能环境中。

许多高性能网站都使用连接分解。你可以通过运行多个单表查询而不是多表连接来分解连接,然后在应用程序中执行连接。

《高性能 MySQL》第 2 版 | O'Reilly

在 Prisma ORM 5.7.0 版本之前,Prisma ORM 将始终使用应用程序级别的 JOIN 策略。但是,通过 5.7.0 版本,我们现在允许你为你的用例选择最佳的 JOIN 策略,确保你始终可以为你的查询获得最佳性能。

ORM 基准测试:没有明显的性能差异

在所有这些改进之后,我们想知道与其他 ORM 库相比,Prisma ORM 在性能方面的表现如何。因此,我们创建了透明的基准测试,比较了 TypeORM、Drizzle ORM 和 Prisma ORM 的查询性能。

基准测试仓库是开源的,我们邀请所有人重现结果并与我们分享。

那么,基准测试显示了什么?

TLDR:根据我们收集的数据,不可能得出结论说一个 ORM 总是 比其他 ORM 表现更好。相反,它取决于相应的查询、数据集、架构以及执行查询的基础设施。

你可以在此处阅读有关基准测试的设置、方法和结果的更多信息:性能基准测试:比较 TypeScript ORM 和数据库的查询延迟

使用 Prisma Optimize 让你的查询更快

从运行基准测试中得到的一个主要启示是,无论你使用什么工具,都有可能编写出快速和慢速的查询。这意味着,最终,确保数据库查询速度的大部分负担实际上都落在开发人员自己身上。

为了确保使用 Prisma ORM 的开发人员尽可能地优化查询速度,我们最近推出了 Prisma Optimize — 一个分析你通过 Prisma ORM 发送到数据库的查询,并为你提供改进建议的工具。

误解 2:你不能使用底层数据库功能

Prisma ORM 本质上是一个 ORM,它提供了对 SQL 更高层次的抽象,以提高开发人员在处理数据库时的生产力、信心和整体体验。

这种更高层次的抽象体现在人类可读的 Prisma 模式(用于描述数据库的结构)和直观的 Prisma Client API(用于查询数据库)。

然而,鉴于抽象有时也会使人们无法访问底层技术的功能(在 Prisma ORM 的情况下:数据库),因此需要一个合适的“逃生舱口”来降级到较低的抽象级别。

因此,为了不牺牲在更高级的场景或边缘情况下可能需要的重要功能,Prisma ORM 为开发人员提供了方便的回退方案,以便访问数据库的底层功能。

自定义迁移让开发人员可以使用任何 SQL 功能

虽然不可能在 Prisma 模式中表示数据库可能具有的所有功能,但你仍然可以通过自定义由 Prisma Migrate 生成的迁移文件来利用这些功能。

为此,你可以在创建新迁移时简单地使用 --create-only 标志,并在将其应用于数据库之前对其进行编辑。

通过使用自定义迁移,你可以自由地操作数据库模式,同时确保所有更改都由 Prisma Migrate 执行并在其迁移历史记录中跟踪。

在 Prisma ORM 中编写类型安全的 SQL

当涉及到查询时,开发人员主要有两种方式可以降级到原始 SQL,并编写无法使用更高级别的查询 API 表示的查询。

TypedSQL:使原始 SQL 类型安全

Prisma ORM 现在为你提供了两全其美的选择:对于大多数查询来说,方便的高级抽象以及用于原始 SQL 的灵活、类型安全的“逃生舱口”。

考虑一下你可能需要在应用程序中编写的原始 SQL 查询的这个示例

在生成步骤之后,你将能够通过 Prisma Client 中新的 $queryRawTyped 方法使用 conversionByVariant 查询

在我们的博客上了解更多信息:宣布推出 TypedSQL:使用 Prisma ORM 使你的原始 SQL 查询类型安全

使用 Kysely SQL 查询构建扩展

另一种替代方法是使用 Kysely 的 Prisma Client 扩展,这允许开发人员使用其 TypeScript API 构建 SQL 查询。例如,使用 Kysely 扩展,你可以使用 Prisma 编写如下 SQL 查询

这使你无需离开 TypeScript 和 Prisma ORM 即可编写高级 SQL 查询。

有趣的事实:Kysely 的核心维护者 Igal 最近加入了我们在 Prisma 的团队 😄

误解 3:Prisma ORM 在底层使用 GraphQL

根据你在 Prisma 社区中存在的时间长短,你可能会对此感到惊讶:Prisma 曾经是一个名为 Graphcool 的 GraphQL 后端即服务提供商

在 2018 年,Graphcool 更名为 Prisma,并从 API 层降级到数据库的“抽象阶梯”。

第一个版本的 Prisma(在它成为 ORM 之前)是你的 API 服务器和数据库之间的 CRUD GraphQL 层

在这一点上,Prisma 1 提供的主要价值是方便的数据建模、迁移查询,所有这些都是通过 GraphQL 完成的。

为了简化 Prisma 的使用并避免用户设置和维护完全独立的服务器,我们用 Rust 重写了 Prisma 的 GraphQL 引擎,使其可以作为可通过 npm install 下载的二进制文件使用

查询引擎在应用程序服务器上作为附加进程运行 GraphQL 服务器。开发人员使用 Prisma Client 与之交互,并编写 TypeScript 查询。这是 Prisma ORM 的初始架构。

从那时起,我们对该架构进行了无数次优化。最值得注意的是,我们为 Rust 和 TypeScript 之间的通信引入了 N-API,将 GraphQL 替换为自定义的 基于 JSON 的线路协议,启用了 JS 原生数据库驱动程序,等等!

如今,Prisma ORM 中不再有 GraphQL 的残留 — 而且我们也不会止步于此,我们将继续改进 Prisma ORM 的架构。我们的下一步是将从 Rust 生成 SQL 的繁重工作的查询引擎转移到 TypeScript,使 Prisma ORM 更加高效。

误解 4:Prisma Client 必须位于 node_modules

开发人员对 Prisma ORM 的一个常见误解是,生成的 Prisma Client 库必须位于 node_modules 中。

但是,node_modules 只是提供熟悉开发体验并启用简单导入的默认位置

通过在 generator 代码块上提供自定义的 output 路径,可以轻松自定义该位置

在这种情况下,你需要调整 import 语句并从文件系统中导入 Prisma Client。考虑到上面的示例,import 现在看起来像这样

当你正在使用单体存储库或其他特殊环境,其中将 Prisma Client 生成到 node_modules 中可能会导致问题时,这非常有用。

误解 5:Prisma 在无服务器/边缘环境中工作不佳

在设计 Prisma ORM 时,无服务器和边缘部署仍然是早期和新兴技术。从那时起,它们已成为许多开发团队依赖的流行部署模型。

Prisma ORM 的初始架构,带有查询引擎二进制文件和内部 GraphQL 服务器,并未针对无服务器环境进行优化,并且存在许多问题

  • 由于基于 GraphQL 的线路协议而导致的冷启动速度缓慢。
  • 无法使用现代数据库提供商(如 Neon 和 PlanetScale)的无服务器驱动程序;这完全阻止了在边缘使用 Prisma Client。
  • 由于查询引擎二进制文件而导致的大包大小。
  • 如果本地计算机与目标计算机不同,则需要声明 binaryTargets,这增加了复杂性。

我们已经认识到所有这些问题,并且随着时间的推移,实施了解决方案并大大提高了 Prisma ORM 在无服务器环境中的 DX

  • 由于我们从查询引擎内部移除了 GraphQL,并且将冷启动速度提高了 9 倍,冷启动不再是问题。
  • 现在,由于驱动程序适配器,无服务器和其他 JS 原生数据库驱动程序(如 pg)可以与 Prisma ORM 一起使用。
  • 我们将 Prisma ORM 的包大小缩小到小于 1MB,从而可以在主要边缘函数提供商(如 Cloudflare,其免费计划的限制为 3MB)的免费计划中使用它。
  • ……并且我们正在努力进行进一步的改进:从 Rust 迁移到 TypeScript 将消除声明 binaryTargets 的需要,并总体上使 Prisma ORM 的部署比以往任何时候都更加顺畅。

帮助我们使 Prisma ORM 成为最好的数据库库 💚

在 Prisma,我们非常重视我们从社区收到的反馈!虽然一些关于 Prisma ORM 的误解可能在过去是真实的,但我们听取了用户的意见,并一直在努力改进围绕它们的情况。

如果你有兴趣了解更多关于我们开源治理方法的信息,请查看 Prisma ORM 宣言

我们将继续努力使 Prisma ORM 成为 TypeScript 生态系统中性能最高的数据库库,并具有最佳的 DX。通过 GitHubDiscordX 告诉我们你希望看到哪些其他改进 🙌

如果你对 Prisma ORM 感到兴奋,那么当你看到其中一些在开发者社区中出现时,可以通过分享这篇文章来帮助我们澄清这些误解。此外,如果还有其他你想让我们揭穿的误解,请告诉我们

不要错过下一篇文章!

注册 Prisma 时事通讯