跳至主要内容

数据库连接

数据库可以处理有限数量的并发连接。每个连接都需要 RAM,这意味着仅仅增加数据库连接限制而不扩展可用资源

  • ✔ 可能会允许更多进程连接,
  • ✘ 会显著影响数据库性能,并可能由于内存不足错误导致数据库关闭

您的应用程序管理连接的方式也会影响性能。本指南介绍了如何在无服务器环境长时间运行的进程中处理连接管理。

警告

本指南重点介绍关系型数据库以及如何配置和调整 Prisma ORM 连接池(MongoDB 使用 MongoDB 驱动程序连接池)。

长时间运行的进程

长时间运行的进程示例包括托管在 Heroku 或虚拟机等服务上的 Node.js 应用程序。在长时间运行的环境中,请使用以下清单作为连接管理的指南

对于长时间运行的进程,建议的起始连接池大小(connection_limit默认池大小num_physical_cpus * 2 + 1)÷ 应用程序实例数量

信息

num_physical_cpus 指的是您的应用程序运行机器的 CPU 数量。

如果您有一个应用程序实例

  • 默认连接池大小默认适用(num_physical_cpus * 2 + 1) - 您无需设置 connection_limit 参数。
  • 您可以选择性地调整连接池大小

如果您有多个应用程序实例

长时间运行应用程序中的PrismaClient

长时间运行的应用程序中,我们建议您

  • ✔ 创建一个PrismaClient实例并在整个应用程序中重复使用
  • 仅在开发环境中PrismaClient分配给全局变量,以防止热重载创建新实例

重复使用单个PrismaClient实例

要重复使用单个实例,请创建一个导出PrismaClient对象的模块

client.ts
import { PrismaClient } from '../prisma/generated/client'

let prisma = new PrismaClient()

export default prisma

该对象在模块首次导入时被缓存。后续请求将返回缓存的对象,而不是创建新的PrismaClient

app.ts
import prisma from './client'

async function main() {
const allUsers = await prisma.user.findMany()
}

main()

您不必完全复制上述示例 - 目标是确保PrismaClient被缓存。例如,您可以在context对象中实例化PrismaClient,然后将该对象传递给 Express 应用程序

不要显式$disconnect()

在持续服务请求的长时间运行应用程序中,您无需显式$disconnect()。打开新连接需要时间,如果您在每次查询后断开连接,可能会减慢应用程序的速度。

防止热重载创建新的PrismaClient实例

Next.js这样的框架支持已更改文件的热重载,这使您无需重新启动即可查看应用程序的更改。但是,如果框架刷新了负责导出PrismaClient的模块,这可能导致在开发环境中产生额外的不需要的PrismaClient实例

作为一种变通方法,您可以仅在开发环境中将PrismaClient存储为全局变量,因为全局变量不会被重新加载

client.ts
import { PrismaClient } from '../prisma/generated/client'

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }

export const prisma =
globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

您导入和使用 Prisma 客户端的方式不会改变

app.ts
import { prisma } from './client'

async function main() {
const allUsers = await prisma.user.findMany()
}

main()

每个 CLI 命令创建的连接数

在 Postgres、MySQL 和 SQLite 的本地测试中,每个 Prisma CLI 命令通常使用单个连接。下表显示了这些测试中观察到的范围。您的环境可能会产生略微不同的结果。

命令连接数描述
迁移状态1检查迁移状态
迁移开发1–4在开发中应用待处理的迁移
迁移差异1–2将数据库架构与迁移历史进行比较
迁移重置1–2重置数据库并重新应用迁移
迁移部署1–2在生产中应用待处理的迁移
db pull1将数据库架构拉取到 Prisma 架构中
db push1–2将 Prisma 架构推送到数据库
db execute1执行原始 SQL 命令
db seed1使用初始数据填充数据库

无服务器环境 (FaaS)

无服务器环境的示例包括托管在 AWS Lambda、Vercel 或 Netlify Functions 上的 Node.js 函数。请使用以下清单作为无服务器环境中连接管理的指南

无服务器挑战

在无服务器环境中,每个函数都会创建自己的PrismaClient实例,并且每个客户端实例都有自己的连接池。

考虑以下示例,一个 AWS Lambda 函数使用PrismaClient连接到数据库。connection_limit3

An AWS Lambda function connecting to a database.

流量高峰导致 AWS Lambda 额外生成两个 Lambda 函数来处理增加的负载。每个 Lambda 函数都会创建一个PrismaClient实例,每个实例的connection_limit3,这导致数据库的最大连接数为9

Three AWS Lambda function connecting to a database.

200个并发函数(因此可能有600个连接)响应流量高峰📈,会很快耗尽数据库连接限制。此外,任何暂停的函数默认会保持其连接开放,并阻止其他函数使用它们。

  1. 首先connection_limit设置为1
  2. 如果较小的连接池大小不足,请考虑使用PgBouncer 等外部连接池

无服务器环境中推荐的连接池大小(connection_limit)取决于

没有外部连接池

如果您没有使用外部连接池,首先将连接池大小(connection_limit)设置为1,然后进行优化。每个传入请求都会启动一个短命的 Node.js 进程,许多并发函数和较高的connection_limit可能会在流量高峰期间迅速耗尽数据库连接限制

以下示例演示了如何在连接 URL 中将connection_limit设置为 1

postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public&connection_limit=1
提示

如果您正在使用 AWS Lambda 并且没有配置connection_limit,请参阅以下 GitHub 问题以获取有关预期默认连接池大小的信息:https://github.com/prisma/docs/issues/667

使用外部连接池

如果您正在使用外部连接池,请使用默认连接池大小(num_physical_cpus * 2 + 1)作为起点,然后调整连接池大小。外部连接池应防止流量高峰使数据库过载。

优化并行请求

如果您在连接池大小设置为 1 的情况下很少或从未超出数据库连接限制,您可以进一步优化连接池大小。考虑一个并行发送查询的函数

Promise.all() {
query1,
query2,
query3
query4,
...
}

如果connection_limit为1,此函数将被迫串行(一个接一个)而不是并行发送查询。这会减慢函数处理请求的能力,并可能导致连接池超时错误。调整connection_limit参数直到流量高峰

  • 不会耗尽数据库连接限制
  • 不会导致连接池超时错误

无服务器环境中的PrismaClient

在处理程序外部实例化PrismaClient

在函数处理程序作用域之外实例化PrismaClient,以增加重用机会。只要处理程序保持“热”(在使用中),连接就有可能被重用

import { PrismaClient } from '../prisma/generated/client'

const client = new PrismaClient()

export async function handler() {
/* ... */
}

不要显式$disconnect()

无需在函数末尾显式$disconnect(),因为容器可能会被重用。打开新连接需要时间,并会减慢函数处理请求的能力。

其他无服务器注意事项

容器重用

无法保证函数的后续附近调用会命中同一个容器——例如,AWS 可以随时选择创建一个新容器。

代码应假定容器是无状态的,并且仅在连接不存在时才创建连接——Prisma Client JS 已经实现了此逻辑。

僵尸连接

被标记为“待移除”且未被重用的容器仍然保持连接开放,并可能保持该状态一段时间(AWS 未知且未记录)。这可能导致数据库连接的利用率低于最佳水平。

一个潜在的解决方案是清理空闲连接serverless-mysql实现了这个想法,但不能与 Prisma ORM 一起使用)。

并发限制

根据您的无服务器并发限制(并行运行的无服务器函数数量),您可能仍然会耗尽数据库的连接限制。当太多函数并发调用时,每个函数都有自己的连接池,最终会耗尽数据库连接限制。为了防止这种情况,您可以将无服务器并发限制设置为低于数据库最大连接限制除以每个函数调用使用的连接数的值(因为您可能希望能够从其他客户端连接以实现其他目的)。

优化连接池

如果查询引擎无法在达到时间限制之前处理队列中的查询,您将会在日志中看到连接池超时异常。连接池超时可能发生在以下情况

  • 许多用户同时访问您的应用程序
  • 您并行发送大量查询(例如,使用await Promise.all()

如果您在配置推荐的连接池大小后仍然持续遇到连接池超时,您可以进一步调整connection_limitpool_timeout参数。

增加连接池大小

注意

从 Prisma ORM v7 开始,驱动程序适配器是关系型数据库的默认选项,可提供更好的性能和开发者体验。在此处了解更多信息

在 Prisma ORM v7 中,默认生成器配置使用驱动程序适配器:

generator client {
provider = "prisma-client"
output = "../generated/prisma"
}

使用驱动程序适配器时,连接池配置由您提供的 Node.js 驱动程序(如pgmariadbmssql)处理。有关 v7 默认值和配置详细信息,请参阅连接池指南

Prisma ORM 也有其自己的可配置超时,与数据库驱动程序超时不同。如果您遇到超时错误,并且不确定它来自特定的驱动程序还是来自 Prisma Client,请参阅Prisma Client 超时和事务选项文档

您可以在我们的博客上阅读有关此更改的性能和开发体验改进

增加连接池大小允许查询引擎并行处理更多查询。请注意,您的数据库必须能够支持增加的并发连接数,否则您将耗尽数据库连接限制

要增加连接池大小,请手动将connection_limit设置为更高的数字

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public&connection_limit=40"
}

注意:在无服务器环境中将connection_limit设置为 1 是推荐的起点,但此值也可以调整

增加连接池超时

增加连接池超时时间可以给查询引擎更多时间来处理队列中的查询。您可以在以下场景中考虑这种方法

  • 您已经增加了connection_limit
  • 您确信队列不会超出某个大小,否则您最终会耗尽 RAM

要增加连接池超时,请将pool_timeout参数设置为大于默认值(10 秒)的值

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=20"
}

禁用连接池超时

禁用连接池超时可防止查询引擎在等待连接 x 秒后抛出异常,并允许队列累积。您可以在以下场景中考虑此方法

  • 您正在有限的时间内提交大量查询 - 例如,作为导入或更新数据库中每个客户的任务的一部分。
  • 您已经增加了connection_limit
  • 您确信队列不会超出某个大小,否则您最终会耗尽 RAM

要禁用连接池超时,请将pool_timeout参数设置为0

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=0"
}

外部连接池

Prisma Accelerate和 PgBouncer 这样的连接池可以防止您的应用程序耗尽数据库的连接限制。

为了让 Prisma Client 保持在池连接上,同时允许 Prisma CLI 命令(例如,迁移或内省)直接连接,请定义两个环境变量

.env
# Connection URL to your database using PgBouncer.
DATABASE_URL="postgres://root:password@127.0.0.1:54321/postgres?pgbouncer=true"

# Direct connection URL to the database used for Prisma CLI commands.
DIRECT_URL="postgres://root:password@127.0.0.1:5432/postgres"

配置prisma.config.ts指向直接连接字符串。Prisma CLI 命令始终从此配置中读取。

prisma.config.ts
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'

export default defineConfig({
schema: 'prisma/schema.prisma',
datasource: {
url: env('DIRECT_URL'),
},
})

在运行时,使用驱动程序适配器(例如,@prisma/adapter-pg)实例化 Prisma Client,该适配器使用池连接字符串

src/db/client.ts
import { PrismaClient } from '../prisma/generated/client'
import { PrismaPg } from '@prisma/adapter-pg'

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
export const prisma = new PrismaClient({ adapter })

Prisma Accelerate

Prisma Accelerate是 Prisma 构建的一个托管的外部连接池,它集成在Prisma 数据平台中,并为您处理连接池。

PgBouncer

PostgreSQL 只支持一定数量的并发连接,当服务使用量增加时,这个限制很快就会达到——特别是在无服务器环境中。

PgBouncer在 Prisma Client 和数据库之间,持有一个到数据库的连接池,并通过代理传入的客户端连接。这减少了数据库在任何给定时间必须处理的进程数量。PgBouncer 将有限数量的连接传递给数据库,并在连接可用时将额外连接排队等待传递。要使用 PgBouncer,请参阅使用 PgBouncer 配置 Prisma Client

AWS RDS 代理

由于 AWS RDS 代理锁定连接的方式,与 Prisma Client 一起使用时,它不提供任何连接池优势

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