Skip to content

数据库连接

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

连接池的工作原理

  • 查询引擎用 可配置池大小 和 池超时 实例化一个连接池。

  • 查询引擎创建一个连接并将其添加到连接池中。

  • 当查询到来时,查询引擎会从池中保留一个连接来处理查询。

  • 如果连接池中没有可用的空闲连接,则查询引擎会打开额外的数据库连接并将其添加到连接池中,直到数据库连接数达到 connection_limit 定义的限制。

  • 如果查询引擎无法从池中保留连接,查询将添加到内存中的 FIFO(先进先出)队列中。FIFO 意味着查询按照进入队列的顺序进行处理。

  • 如果查询引擎无法在 时限 之前处理队列中的查询,则会针对该查询引发错误代码为 P2024 的异常,并继续处理队列中的下一个查询。

连接池大小

默认

bash
CPU的个数 * 2 +1

举个例子: 你的服务器有 4 个物理 CPU,则你的连接池将包含 9 个链接(4*2+1=9)

设置连接池大小

  • 5 可以换成你需要的数量
ts
datasource db {
  provider = "mysql"
  url      = "mysql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5"
}

查看连接池大小

ts
const prisma = new PrismaClient({
  log: ["info"],
});

async function main() {
  const users = await prisma.user.findMany(); // user 换成你的表名
  console.log(users);
}
  • 这样就会打印出来
bash
prisma:info Starting a mysql pool with 17 connections.

连接池超时

  • 默认超时

默认连接池超时时间为 10 秒。如果查询引擎在这段时间内没有从数据库连接池获取连接,它将引发异常并继续执行队列中的下一个查询

  • 设置连接池超时时间

你可以通过在数据库连接 URL 中显式设置 pool_timeout 参数来指定池超时。在以下示例中,池在 20 秒后超时:

ts
datasource db {
  provider = "mysql"
  url      = "mysql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=20"
}
  • 禁用连接池超时

你可以通过将 pool_timeout 参数设置为 0 来禁用连接池超时:

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

链接管理

  • 链接方式

注意

  1. $connect() 开启链接

  2. $disconnect() // 关闭链接

大多数时候 你不需要显示调用,当你运行第一个查询的时候,PrismaClient会自动链接,并创建连接池

$connect

  • 显示调用
ts
const prisma = new PrismaClient();

// run inside `async` function
await prisma.$connect();

$disconnect

  • 流程

注意

当你调用$disconnect 的时候

  1. 运行 beforeExit 钩子函数

  2. 结束查询并关闭所有的连接

  • 显示调用

你应该显示调用$disconnect就是脚本:

注意

  1. 不经常运行(例如,每晚发送电子邮件的计划作业),这意味着它不会从与数据库的长时间运行连接中受益

  2. 存在于长时间运行的应用的上下文中,例如后台服务。如果应用永远不会关闭,Prisma 客户端永远不会断开连接。

举例如下:

ts
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();
const emailService = new EmailService();

async function main() {
  const allUsers = await prisma.user.findMany();
  const emails = allUsers.map((x) => x.email);

  await emailService.send(emails, "Hello!");
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

退出钩子(一般不用)

  • 仅仅能在二进制查询引擎使用
ts
const prisma = new PrismaClient();

prisma.$on("beforeExit", async () => {
  console.log("beforeExit hook");
  // PrismaClient still available
  await prisma.message.create({
    data: {
      message: "Shutting down server",
    },
  });
});

主从分离

  • 主服务器负责写入 , 从服务器负责读取

mysql 具体的 bin 参考 mysql 章节

prisma 设置主从分离

  • 安装扩展
ts
npm install @prisma/extension-read-replicas
  • 设置读取扩展

通过扩展 Prisma 客户端实例来初始化扩展,并为扩展提供一个指向扩展的 url 选项中的只读副本的连接字符串。

ts
import { PrismaClient } from "../generated/prisma";
import { readReplicas } from "@prisma/extension-read-replicas";
const HuanJing = process.env.DATABASE_URL_REPLICA; // .env文件里面写你的从服务器地址
const prisma = new PrismaClient({
  log: ["info"],
}).$extends(
  readReplicas({
    url: HuanJing || "",
  })
);

async function main() {
  const posts = await prisma.post.findMany(); // user 换成你的表名

  console.log(posts);
}

main()
  .catch((e) => {
    throw e;
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
  • 如果是多个从服务器
ts
import { PrismaClient } from "../generated/prisma";
import { readReplicas } from "@prisma/extension-read-replicas";
const HuanJing = [process.env.DATABASE_URL_REPLICA || "", ""];
const prisma = new PrismaClient({
  log: ["info"],
}).$extends(
  readReplicas({
    url: HuanJing || [],
  })
);
  • 从主数据库进行读取操作

你可以使用 $primary() 方法对主数据库显式执行读取操作:

ts
const posts = await prisma.$primary().post.findMany();
  • 对数据库副本执行操作

你可以使用 $replica() 方法对副本而不是主数据库显式执行查询:

ts
const result = await prisma.$replica().user.findFirst(...)

注意

  1. 这样 写入操作就会走 schema.prisma里面的配置的 db 地址

  2. 读取就会走DATABASE_URL_REPLICA