Skip to main content

数据库连接

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

¥Databases can handle a limited number of concurrent connections. Each connection requires RAM, which means that simply increasing the database connection limit without scaling available resources:

  • ✔ 可能允许更多进程连接,但是

    ¥✔ might allow more processes to connect but

  • ✘ 显着影响数据库性能,并可能导致数据库因内存不足错误而关闭

    ¥✘ significantly affects database performance, and can result in the database being shut down due to an out of memory error

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

¥The way your application manages connections also impacts performance. This guide describes how to approach connection management in serverless environments and long-running processes.

warning

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

¥This guide focuses on relational databases and how to configure and tune the Prisma ORM connection pool (MongoDB uses the MongoDB driver connection pool).

长时间运行的进程

¥Long-running processes

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

¥Examples of long-running processes include Node.js applications hosted on a service like Heroku or a virtual machine. Use the following checklist as a guide to connection management in long-running environments:

建议的连接池大小

¥Recommended connection pool size

对于长时间运行的进程,建议的连接池大小 (connection_limit) 到 从...开始 是默认池大小 (num_physical_cpus * 2 + 1) ÷ 应用实例数。

¥The recommended connection pool size (connection_limit) to start with for long-running processes is the default pool size (num_physical_cpus * 2 + 1) ÷ number of application instances.

信息

num_physical_cpus 是指运行应用的计算机的 CPU 数量。

¥num_physical_cpus refers to the the number of CPUs of the machine your application is running on.

如果你有一个应用实例:

¥If you have one application instances:

  • 默认情况下应用默认池大小 (num_physical_cpus * 2 + 1) - 无需设置 connection_limit 参数。

    ¥The default pool size applies by default (num_physical_cpus * 2 + 1) - you do not need to set the connection_limit parameter.

  • 你可以选择 调整池大小

    ¥You can optionally tune the pool size.

如果你有多个应用实例:

¥If you have multiple application instances:

长时间运行的应用中的 PrismaClient

¥PrismaClient in long-running applications

在长时间运行的应用中,我们建议你:

¥In long-running applications, we recommend that you:

重复使用单个 PrismaClient 实例

¥Re-using a single PrismaClient instance

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

¥To re-use a single instance, create a module that exports a PrismaClient object:

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

let prisma = new PrismaClient()

export default prisma

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

¥The object is cached the first time the module is imported. Subsequent requests return the cached object rather than creating a new PrismaClient:

app.ts
import prisma from './client'

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

main()

你不必完全复制上面的示例 - 目标是确保 PrismaClient 被缓存。例如,你可以 context 对象中实例化 PrismaClient,你可以 传递到 Express 应用

¥You do not have to replicate the example above exactly - the goal is to make sure PrismaClient is cached. For example, you can instantiate PrismaClient in the context object that you pass into an Express app.

不要明确 $disconnect()

¥Do not explicitly $disconnect()

不需要明确 $disconnect() 在一个持续服务请求的长时间运行的应用的上下文中。打开新连接需要时间,如果在每次查询后断开连接,可能会减慢应用的速度。

¥You do not need to explicitly $disconnect() in the context of a long-running application that is continuously serving requests. Opening a new connection takes time and can slow down your application if you disconnect after each query.

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

¥Prevent hot reloading from creating new instances of PrismaClient

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

¥Frameworks like Next.js support hot reloading of changed files, which enables you to see changes to your application without restarting. However, if the framework refreshes the module responsible for exporting PrismaClient, this can result in additional, unwanted instances of PrismaClient in a development environment.

作为解决方法,你可以仅在开发环境中将 PrismaClient 存储为全局变量,因为全局变量不会重新加载:

¥As a workaround, you can store PrismaClient as a global variable in development environments only, as global variables are not reloaded:

client.ts
import { PrismaClient } from '@prisma/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 Client 的方式不会改变:

¥The way that you import and use Prisma Client does not change:

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

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

main()

每个 CLI 命令创建的连接

¥Connections Created per CLI Command

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

¥In local tests with Postgres, MySQL, and SQLite, each Prisma CLI command typically uses a single connection. The table below shows the ranges observed in these tests. Your environment may produce slightly different results.

命令连接描述
migrate status1检查迁移状态
migrate dev1–4在开发环境中应用待处理的迁移
migrate diff1–2将数据库架构与迁移历史记录进行比较
migrate reset1–2重置数据库并重新应用迁移
migrate deploy1–2在生产环境中应用待处理的迁移
db pull1将数据库模式拉取到 Prisma 模式中
db push1–2将 Prisma 模式推送到数据库
db execute1执行原始 SQL 命令
db seed1使用初始数据填充数据库

无服务器环境 (FaaS)

¥Serverless environments (FaaS)

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

¥Examples of serverless environments include Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions. Use the following checklist as a guide to connection management in serverless environments:

无服务器挑战

¥The serverless challenge

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

¥In a serverless environment, each function creates its own instance of PrismaClient, and each client instance has its own connection pool.

考虑以下示例,其中单个 AWS Lambda 函数使用 PrismaClient 连接到数据库。connection_limit 是 3:

¥Consider the following example, where a single AWS Lambda function uses PrismaClient to connect to a database. The connection_limit is 3:

An AWS Lambda function connecting to a database.

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

¥A traffic spike causes AWS Lambda to spawn two additional lambdas to handle the increased load. Each lambda creates an instance of PrismaClient, each with a connection_limit of 3, which results in a maximum of 9 connections to the database:

Three AWS Lambda function connecting to a database.

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

¥200 concurrent functions (and therefore 600 possible connections) responding to a traffic spike 📈 can exhaust the database connection limit very quickly. Furthermore, any functions that are paused keep their connections open by default and block them from being used by another function.

  1. connection_limit 设置为 1 开始

    ¥Start by setting the connection_limit to 1

  2. 如果较小的池大小还不够,请考虑使用 外部连接池,如 PgBouncer

    ¥If a smaller pool size is not enough, consider using an external connection pooler like PgBouncer

建议的连接池大小

¥Recommended connection pool size

无服务器环境中建议的池大小 (connection_limit) 取决于:

¥The recommended pool size (connection_limit) in serverless environments depends on:

没有外部连接池

¥Without an external connection pooler

如果你不使用外部连接池程序,请首先将池大小 (connection_limit) 设置为 1,然后设置为 optimize。每个传入请求都会启动一个短暂的 Node.js 进程,并且许多具有高 connection_limit 的并发函数可以在流量高峰期间快速耗尽数据库连接限制。

¥If you are not using an external connection pooler, start by setting the pool size (connection_limit) to 1, then optimize. Each incoming request starts a short-lived Node.js process, and many concurrent functions with a high connection_limit can quickly exhaust the database connection limit during a traffic spike.

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

¥The following example demonstrates how to set the connection_limit to 1 in your connection URL:

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

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

¥If you are using AWS Lambda and not configuring a connection_limit, refer to the following GitHub issue for information about the expected default pool size: https://github.com/prisma/docs/issues/667

使用外部连接池

¥With an external connection pooler

如果你使用外部连接池程序,请使用默认池大小 (num_physical_cpus * 2 + 1) 作为起点,然后使用 调整池大小。外部连接池应防止流量高峰导致数据库不堪重负。

¥If you are using an external connection pooler, use the default pool size (num_physical_cpus * 2 + 1) as a starting point and then tune the pool size. The external connection pooler should prevent a traffic spike from overwhelming the database.

优化并行请求

¥Optimizing for parallel requests

如果你很少或从不超出将池大小设置为 1 的数据库连接限制,则可以进一步优化连接池大小。考虑一个并行发送查询的函数:

¥If you rarely or never exceed the database connection limit with the pool size set to 1, you can further optimize the connection pool size. Consider a function that sends queries in parallel:

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

如果 connection_limit 为 1,则该函数被迫串行发送查询(一个接一个)而不是并行发送。这会降低函数处理请求的能力,并可能导致池超时错误。调整 connection_limit 参数直到出现流量峰值:

¥If the connection_limit is 1, this function is forced to send queries serially (one after the other) rather than in parallel. This slows down the function's ability to process requests, and may result in pool timeout errors. Tune the connection_limit parameter until a traffic spike:

  • 不耗尽数据库连接限制

    ¥Does not exhaust the database connection limit

  • 不会导致池超时错误

    ¥Does not result in pool timeout errors

无服务器环境中的 PrismaClient

¥PrismaClient in serverless environments

在处理程序外部实例化 PrismaClient

¥Instantiate PrismaClient outside the handler

实例化 PrismaClient 在函数处理程序的范围之外 以增加重用的机会。只要处理程序保持 'warm'(正在使用),连接就有可能被重用:

¥Instantiate PrismaClient outside the scope of the function handler to increase the chances of reuse. As long as the handler remains 'warm' (in use), the connection is potentially reusable:

import { PrismaClient } from '@prisma/client'

const client = new PrismaClient()

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

不要明确 $disconnect()

¥Do not explicitly $disconnect()

你可以在函数末尾添加 不需要明确 $disconnect(),因为容器可能会被重用。打开新连接需要时间,并且会降低函数处理请求的能力。

¥You do not need to explicitly $disconnect() at the end of a function, as there is a possibility that the container might be reused. Opening a new connection takes time and slows down your function's ability to process requests.

其他无服务器注意事项

¥Other serverless considerations

容器重复利用

¥Container reuse

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

¥There is no guarantee that subsequent nearby invocations of a function will hit the same container - for example, AWS can choose to create a new container at any time.

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

¥Code should assume the container to be stateless and create a connection only if it does not exist - Prisma Client JS already implements this logic.

僵尸连接

¥Zombie connections

标记为 "即将被删除" 且未被重用的容器仍保持连接打开状态,并且可以保持该状态一段时间(未知且未在 AWS 中记录)。这可能会导致数据库连接的利用率不高。

¥Containers that are marked "to be removed" and are not being reused still keep a connection open and can stay in that state for some time (unknown and not documented from AWS). This can lead to sub-optimal utilization of the database connections.

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

¥A potential solution is to clean up idle connections (serverless-mysql implements this idea, but cannot be used with Prisma ORM).

并发限制

¥Concurrency limits

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

¥Depending on your serverless concurrency limit (the number of serverless functions running in parallel), you might still exhaust your database's connection limit. This can happen when too many functions are invoked concurrently, each with its own connection pool, which eventually exhausts the database connection limit. To prevent this, you can set your serverless concurrency limit to a number lower than the maximum connection limit of your database divided by the number of connections used by each function invocation (as you might want to be able to connect from another client for other purposes).

优化连接池

¥Optimizing the connection pool

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

¥If the query engine cannot process a query in the queue before the time limit , you will see connection pool timeout exceptions in your log. A connection pool timeout can occur if:

  • 许多用户同时访问你的应用

    ¥Many users are accessing your app simultaneously

  • 你并行发送大量查询(例如,使用 await Promise.all()

    ¥You send a large number of queries in parallel (for example, using await Promise.all())

如果在配置建议的池大小后始终遇到连接池超时,则可以进一步调整 connection_limitpool_timeout 参数。

¥If you consistently experience connection pool timeouts after configuring the recommended pool size, you can further tune the connection_limit and pool_timeout parameters.

增加池大小

¥Increasing the pool size

注意

v6.7.0 开始,Prisma ORM 具有 queryCompiler 预览功能。

¥As of v6.7.0, Prisma ORM has the queryCompiler Preview feature.

启用后,你的 Prisma 客户端将生成 无需基于 Rust 的查询引擎二进制文件

¥When enabled, your Prisma Client will be generated without a Rust-based query engine binary:

generator client {
provider = "prisma-client-js"
previewFeatures = ["queryCompiler", "driverAdapters"]
}

请注意,驱动适配器 预览功能与 queryCompiler 功能同时启用。使用 queryCompiler 预览功能时,连接池大小由你正在使用的原生 JS 驱动程序设置。

¥Note that the driver adapters Preview feature is required alongside queryCompiler. When using the queryCompiler Preview feature, the connection pool size is set via the native JS driver you are using.

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

¥Increasing the pool size allows the query engine to process a larger number of queries in parallel. Be aware that your database must be able to support the increased number of concurrent connections, otherwise you will exhaust the database connection limit.

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

¥To increase the pool size, manually set the connection_limit to a higher number:

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

注意:建议在无服务器环境中将 connection_limit 设置为 1,但 这个值也可以调整

¥Note: Setting the connection_limit to 1 in serverless environments is a recommended starting point, but this value can also be tuned.

增加池超时

¥Increasing the pool timeout

增加池超时可以让查询引擎有更多时间来处理队列中的查询。你可以在以下场景中考虑此方法:

¥Increasing the pool timeout gives the query engine more time to process queries in the queue. You might consider this approach in the following scenario:

  • 你已经增加了 connection_limit

    ¥You have already increased the connection_limit.

  • 你确信队列不会增长超过一定大小,否则你最终将耗尽 RAM。

    ¥You are confident that the queue will not grow beyond a certain size, otherwise you will eventually run out of RAM.

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

¥To increase the pool timeout, set the pool_timeout parameter to a value larger than the default (10 seconds):

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

禁用池超时

¥Disabling the pool timeout

禁用池超时可防止查询引擎在等待连接 x 秒后引发异常,并允许建立队列。你可以在以下场景中考虑此方法:

¥Disabling the pool timeout prevents the query engine from throwing an exception after x seconds of waiting for a connection and allows the queue to build up. You might consider this approach in the following scenario:

  • 你在有限的时间内提交大量查询 - 例如,作为导入或更新数据库中每个客户的工作的一部分。

    ¥You are submitting a large number of queries for a limited time - for example, as part of a job to import or update every customer in your database.

  • 你已经增加了 connection_limit

    ¥You have already increased the connection_limit.

  • 你确信队列不会增长超过一定大小,否则你最终将耗尽 RAM。

    ¥You are confident that the queue will not grow beyond a certain size, otherwise you will eventually run out of RAM.

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

¥To disable the pool timeout, set the pool_timeout parameter to 0:

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

外部连接池

¥External connection poolers

Prisma 加速 和 PgBouncer 这样的连接池可以防止你的应用耗尽数据库的连接限制。

¥Connection poolers like Prisma Accelerate and PgBouncer prevent your application from exhausting the database's connection limit.

如果你想使用 Prisma CLI 对数据库执行其他操作,例如 迁移和内省,你将需要添加一个环境变量,以在 Prisma 架构的 datasource.directUrl 属性中提供与数据库的直接连接:

¥If you would like to use the Prisma CLI in order to perform other actions on your database ,e.g. migrations and introspection, you will need to add an environment variable that provides a direct connection to your database in the datasource.directUrl property in your Prisma schema:

.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 migrations
DIRECT_URL="postgres://root:password@127.0.0.1:5432/postgres"

然后,你可以更新 schema.prisma 以使用新的直接 URL:

¥You can then update your schema.prisma to use the new direct URL:

schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}

有关 directUrl 字段的更多信息可以找到 此处

¥More information about the directUrl field can be found here.

Prisma 加速

¥Prisma Accelerate

Prisma 加速 是由 Prisma 构建的托管外部连接池,集成在 Prisma 数据平台 中并为你处理连接池。

¥Prisma Accelerate is a managed external connection pooler built by Prisma that is integrated in the Prisma Data Platform and handles connection pooling for you.

PgBouncer

PostgreSQL 只支持一定数量的并发连接,当服务使用量上升时,这个限制会很快达到 - 尤其是在 无服务器环境

¥PostgreSQL only supports a certain amount of concurrent connections, and this limit can be reached quite fast when the service usage goes up – especially in serverless environments.

PgBouncer 拥有一个到数据库的连接池,并通过位于 Prisma 客户端和数据库之间代理传入的客户端连接。这减少了数据库在任何给定时间必须处理的进程数。PgBouncer 将有限数量的连接传递到数据库,并在连接可用时对其他连接进行排队以供传递。要使用 PgBouncer,请参阅 使用 PgBouncer 配置 Prisma 客户端

¥PgBouncer holds a connection pool to the database and proxies incoming client connections by sitting between Prisma Client and the database. This reduces the number of processes a database has to handle at any given time. PgBouncer passes on a limited number of connections to the database and queues additional connections for delivery when connections becomes available. To use PgBouncer, see Configure Prisma Client with PgBouncer.

AWS RDS 代理

¥AWS RDS Proxy

由于 AWS RDS 代理固定连接的方式,与 Prisma 客户端一起使用时会出现 它不提供任何连接池的好处

¥Due to the way AWS RDS Proxy pins connections, it does not provide any connection pooling benefits when used together with Prisma Client.