连接池
查询引擎管理着一个数据库连接的连接池。该连接池在 Prisma Client 打开与数据库的第一个连接时创建,这可以通过以下两种方式之一发生
- 通过显式调用
$connect()
或 - 通过运行第一个查询,该查询在底层调用了
$connect()
关系型数据库连接器使用 Prisma ORM 自己的连接池,而 MongoDB 连接器则使用MongoDB 驱动程序连接池。
自 v6.7.0 起,Prisma ORM 引入了 queryCompiler
预览功能。
启用后,你的 Prisma Client 将在不包含基于 Rust 的查询引擎二进制文件的情况下生成:
generator client {
provider = "prisma-client-js"
previewFeatures = ["queryCompiler", "driverAdapters"]
}
请注意,queryCompiler
功能需要同时启用驱动适配器预览功能。使用 queryCompiler
预览功能时,连接池由你使用的原生 JS 数据库驱动程序维护。
关系型数据库
关系型数据库连接器使用 Prisma ORM 的连接池。连接池具有连接限制和池超时,这些都由连接 URL 参数控制。
连接池的工作原理
以下步骤描述了查询引擎如何使用连接池
- 查询引擎实例化一个连接池,该连接池具有可配置的池大小和池超时。
- 查询引擎创建一个连接并将其添加到连接池中。
- 当有查询传入时,查询引擎从连接池中保留一个连接来处理查询。
- 如果连接池中没有空闲连接可用,查询引擎将打开额外的数据库连接并将其添加到连接池中,直到数据库连接数达到
connection_limit
定义的限制。 - 如果查询引擎无法从连接池中保留连接,查询将按 FIFO(先进先出)顺序添加到内存队列中。FIFO 意味着查询按照它们进入队列的顺序进行处理。
- 如果查询引擎在时间限制之前无法处理队列中的某个查询,它将针对该查询抛出错误代码为
P2024
的异常,并转到队列中的下一个查询。
如果你持续遇到连接池超时错误,你需要优化连接池。
连接池大小
默认连接池大小
默认连接数(池大小)按以下公式计算
num_physical_cpus * 2 + 1
num_physical_cpus
表示你的应用程序运行机器上的物理 CPU 数量。如果你的机器有四个物理 CPU,你的连接池将包含九个连接(4 * 2 + 1 = 9
)。
尽管该公式是一个很好的起点,但推荐的连接限制也取决于你的部署范式——特别是如果你正在使用无服务器环境。
设置连接池大小
你可以通过在数据库连接 URL 中显式设置 connection_limit
参数来指定连接数。例如,在你的 Prisma schema 中使用以下 datasource
配置,连接池将恰好有五个连接
datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5"
}
查看连接池大小
Prisma Client 使用的连接数可以通过日志和指标来查看。
使用 info
日志级别,你可以在 Prisma Client 实例化时记录连接池中打开的连接数。
例如,考虑以下 Prisma Client 实例和调用
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient({
log: ['info'],
})
async function main() {
await prisma.user.findMany()
}
main()
prisma:info Starting a postgresql pool with 21 connections.
当 PrismaClient
类实例化时,日志通知 stdout
已启动一个包含 21 个连接的连接池。
请注意,log: ['info']
生成的输出在任何版本中都可能在不通知的情况下发生变化。如果你在应用程序或正在构建的工具中依赖此输出,请注意这一点。
如果你需要更深入地了解连接池的大小以及正在使用和空闲连接的数量,你可以使用指标功能(目前处于预览阶段)。
考虑以下示例
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
await Promise.all([prisma.user.findMany(), prisma.post.findMany()])
const metrics = await prisma.$metrics.json()
console.dir(metrics, { depth: Infinity })
}
main()
{
"counters": [
// ...
{
"key": "prisma_pool_connections_open",
"labels": {},
"value": 2,
"description": "Number of currently open Pool Connections"
}
],
"gauges": [
// ...
{
"key": "prisma_pool_connections_busy",
"labels": {},
"value": 0,
"description": "Number of currently busy Pool Connections (executing a datasource query)"
},
{
"key": "prisma_pool_connections_idle",
"labels": {},
"value": 21,
"description": "Number of currently unused Pool Connections (waiting for the next datasource query to run)"
},
{
"key": "prisma_pool_connections_opened_total",
"labels": {},
"value": 2,
"description": "Total number of Pool Connections opened"
}
],
"histograms": [
/** ... **/
]
}
有关指标输出中可用内容的更多详细信息,请参阅关于指标部分。
连接池超时
默认池超时
默认连接池超时时间为 10 秒。如果查询引擎在该时间内未从数据库连接池获取到连接,它将抛出异常并转到队列中的下一个查询。
设置连接池超时
你可以通过在数据库连接 URL 中显式设置 pool_timeout
参数来指定连接池超时时间。在以下示例中,连接池将在 2
秒后超时
datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=2"
}
禁用连接池超时
通过将 pool_timeout
参数设置为 0
来禁用连接池超时
datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=0"
}
你可以选择禁用连接池超时,如果查询必须保留在队列中——例如,如果你正在并行导入大量记录,并且确信在作业完成之前队列不会耗尽所有可用 RAM。
MongoDB
MongoDB 连接器不使用 Prisma ORM 连接池。连接池由 MongoDB 驱动程序内部管理,并通过连接字符串参数配置。
外部连接池
你不能将 connection_limit
增加到超出底层数据库所能支持的范围。这在无服务器环境中尤其是一个挑战,因为每个函数都管理着一个 PrismaClient
实例及其自己的连接池。
考虑引入一个像 PgBouncer 这样的外部连接池,以防止你的应用程序或函数耗尽数据库连接限制。
手动数据库连接处理
使用 Prisma ORM 时,数据库连接在引擎层面进行处理。这意味着它们不向开发者暴露,也无法手动访问它们。