2024年12月19日

关于 Prisma ORM 的 5 大迷思

揭秘关于 Prisma ORM 的五个常见误解。本文将辟谣、探究其起源,并有趣地辨别事实与虚构。

Top 5 Myths about 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 关键字向数据库发送单个查询,并让数据由数据库直接连接

Blog image

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

在应用程序级别连接时,你向数据库发送多个针对单个表的查询,然后在应用程序中自行连接数据。

Blog image

何时使用哪种?

根据您的用例、数据集、模式和许多其他因素,一种策略可能比另一种更具性能。应用程序级别的连接方法也称为连接分解,常用于高性能环境。

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

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

直到 Prisma ORM 5.7.0 版本之前,Prisma ORM 总是使用应用程序级别的 JOIN 策略。然而,随着 5.7.0 版本的发布,我们现在允许您根据用例选择最佳的 JOIN 策略,确保您始终能够获得最佳的查询性能。

ORM 基准测试:无重大性能差异

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

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

Blog image

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

总结:根据我们收集的数据,无法断定某个 ORM 总是比其他 ORM 表现更好。相反,它取决于具体的查询、数据集、模式以及执行查询的基础设施。

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

使用 Prisma Optimize 加快你的查询速度

运行基准测试的一个主要发现是,无论使用何种工具,都有可能编写出快速或慢速的查询。这意味着,最终,确保数据库查询快速运行的重担很大程度上落在了开发者自己身上。

Blog image

为了确保使用 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 后端即服务提供商。

Blog image

2018 年,Graphcool 更名为 Prisma,并从 API 层“下探”到数据库层。

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

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

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

查询引擎在应用程序服务器上以 side-car 进程的形式运行 GraphQL 服务器。开发者使用 Prisma Client 与其交互并编写 TypeScript 查询。这是 Prisma ORM 的初始架构。

从那时起,我们对架构进行了无数优化。最值得注意的是,我们引入了N-API用于 Rust 和 TypeScript 之间的通信,用自定义的基于 JSON 的线协议取代了 GraphQL,实现了JS 原生数据库驱动的使用,等等!

如今,Prisma ORM 中已没有任何 GraphQL 的残余——我们也不会止步于此,我们将继续改进 Prisma ORM 的架构。我们的下一步是将负责生成 SQL 的查询引擎从 Rust 迁移到 TypeScript,使 Prisma ORM 更加高效。

迷思 4:Prisma Client 必须存在于 node_modules

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

然而,node_modules 只是默认位置,旨在提供熟悉的开发者体验并实现简单的导入。

该位置可以通过在 generator 块上提供自定义的 output 路径来轻松定制。

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

当您在 monorepo 或其他特殊环境中工作时,这会非常有用,因为将 Prisma Client 生成到 node_modules 可能会导致问题。

迷思 5:Prisma 在 Serverless/Edge 环境下表现不佳

Prisma ORM 设计之初,Serverless 和 Edge 部署仍是早期新兴技术。自那时起,它们已成为许多开发团队依赖的流行部署模型。

Prisma ORM 的初始架构,包含 Query Engine 二进制文件和内部 GraphQL 服务器,并未针对 Serverless 环境进行优化,存在诸多问题:

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

我们已经认识到所有这些问题,并随着时间的推移,实施了解决方案,大大改善了 Prisma ORM 在 Serverless 环境中的开发体验。

  • 冷启动问题已不复存在,因为我们从查询引擎内部移除了 GraphQL,并将冷启动速度提升了 9 倍
  • 现在可以通过驱动适配器与 Prisma ORM 一起使用 Serverless 和其他JS 原生数据库驱动(如 pg)。
  • 我们已将 Prisma ORM 的捆绑包大小减小到 1MB 以下,使其可以在主要 Edge 函数提供商的免费计划中使用(例如 Cloudflare,他们的免费计划限制为 3MB)。
  • ……我们正在努力进行进一步的改进:从 Rust 到 TypeScript 的迁移将消除声明 binaryTargets 的需要,并总体上使 Prisma ORM 的部署比以往任何时候都更加顺畅。

帮助我们把 Prisma ORM 打造成最好的数据库库 💚

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

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

我们将继续努力,使 Prisma ORM 成为 TypeScript 生态系统中性能最佳、开发体验最好的数据库库。请通过GitHubDiscordX 告诉我们您还希望看到哪些改进 🙌

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

不要错过下一篇文章!

订阅 Prisma 新闻通讯

© . This site is unofficial and not affiliated with Prisma Data, Inc.