为什么 Supabase 需要两个数据库 URL:连接池详解
如果你把 Next.js 应用(使用 Prisma)部署到 Vercel,并连接 Supabase(或 Neon),你可能见过类似配置:
DATABASE_URL="postgresql://...@pooler.supabase.com:6543/postgres?pgbouncer=true"
DIRECT_URL="postgresql://...@pooler.supabase.com:5432/postgres"
两个 URL?不同端口?pgbouncer=true 是什么?下面拆解一下。
问题:连接数上限
PostgreSQL 对并发连接数有硬性限制。Supabase 的免费层大约是 60 个连接。听起来不少,对吧?
关键在于:serverless 函数会在每次请求时新建数据库连接。
当 Vercel 函数启动时会连接 Postgres,结束后连接可能还会停留一段时间。流量一上来,就会看到:
Error: too many connections for role "postgres"
即使只有 60 个连接,在流量高峰期也可能几秒内耗尽。
解决方案:连接池
这就是 PgBouncer 的用武之地。它是一个轻量级连接池,位于应用与 PostgreSQL 之间。
没有连接池
┌──────────────┐
│ Request 1 │──────┐
├──────────────┤ │
│ Request 2 │──────┼────▶ PostgreSQL (60 connection limit)
├──────────────┤ │
│ Request 3 │──────┘
└──────────────┘
Each request = 1 connection
60 requests = database at capacity
使用 PgBouncer
┌──────────────┐ ┌───────────┐
│ Request 1 │──────┤ │
├──────────────┤ │ PgBouncer │────▶ PostgreSQL
│ Request 2 │──────┤ (pooler) │ (60 connections)
├──────────────┤ │ │
│ Request 3 │──────┤ │
└──────────────┘ └───────────┘
1000s of requests share 60 connections
PgBouncer 会维护一组数据库连接池,并在请求之间智能复用。你的应用可以同时服务上千并发用户,而数据库实际连接数仍然很少。
为什么需要两个 URL?
现在来到最容易困惑的部分:为什么既要 DATABASE_URL 又要 DIRECT_URL?
DATABASE_URL(端口 6543)
它通过 PgBouncer 连接,使用事务级连接池模式:
- 连接在请求之间共享
- 事务开始时分配连接,结束后释放
- 对高并发应用非常高效
用于: 所有运行时查询(SELECT、INSERT、UPDATE、DELETE)
DIRECT_URL(端口 5432)
它直接连到 PostgreSQL,绕过连接池:
- 每个连接专属一个客户端
- 支持所有 PostgreSQL 特性
- 效率低一些,但某些操作必须使用
用于: 数据库迁移和结构变更
为什么迁移必须用直连?
PgBouncer 的事务池模式有一些限制,不支持:
- 预编译语句(prepared statements)
- 咨询锁(advisory locks)
- 会话级设置(会话内持久的 SET 命令)
Prisma 的迁移依赖咨询锁来防止并发迁移:
-- Prisma 内部会这样做
SELECT pg_advisory_lock(72707369); -- 防止竞态的锁
-- ... 执行迁移 ...
SELECT pg_advisory_unlock(72707369);
但在 PgBouncer 下,你的“会话”可能在每条查询时都换了连接,所以这些锁不会生效。这就是迁移必须走直连的原因。
pgbouncer=true 参数
当你在连接字符串里加上 ?pgbouncer=true,你是在告诉 Prisma:
“我通过连接池连接,请按连接池模式调整行为。”
Prisma 会:
- 禁用预编译语句 —— PgBouncer 的事务池模式不支持
- 调整连接处理方式 —— 适配连接池约束
如果缺少这个参数,可能会出现类似错误:
prepared statement "s0" already exists
拼起来看整体
完整的 Prisma 配置如下:
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL") // 运行时查询走连接池
directUrl = env("DIRECT_URL") // 迁移走直连
}
以及环境变量:
# 运行时查询(通过 PgBouncer)
DATABASE_URL="postgresql://user:pass@pooler.supabase.com:6543/postgres?pgbouncer=true"
# 迁移(直连 PostgreSQL)
DIRECT_URL="postgresql://user:pass@pooler.supabase.com:5432/postgres"
实际运行流程
开发阶段
npx prisma migrate dev
Prisma 使用 DIRECT_URL 来:
- 获取咨询锁
- 应用结构变更
- 释放锁
部署阶段(Vercel)
npx prisma migrate deploy && npx prisma generate && npm run build
prisma migrate deploy→ 使用DIRECT_URL(端口 5432)prisma generate→ 生成客户端(不需要 DB 连接)- 应用运行时 → 使用
DATABASE_URL(端口 6543)
运行时
每个 API 路由、服务端组件或 server action 都会使用连接池:
// 这里会自动使用 DATABASE_URL
const users = await prisma.user.findMany();
快速对照
| 方面 | DATABASE_URL | DIRECT_URL |
|---|---|---|
| 端口 | 6543 | 5432 |
| 经过 | PgBouncer | 直连 Postgres |
| 连接共享 | 是(连接池) | 否(专属) |
| 预编译语句 | 禁用 | 启用 |
| 咨询锁 | 不支持 | 支持 |
| 用途 | 应用查询 | 迁移 |
| 需要参数 | ?pgbouncer=true | 无 |
常见坑
1. 忘记在 schema.prisma 里写 directUrl
// 错误 - 迁移会失败
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// 正确
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
2. 缺少 pgbouncer 参数
# 错误 - 会触发 prepared statement 报错
DATABASE_URL="postgresql://...@pooler.supabase.com:6543/postgres"
# 正确
DATABASE_URL="postgresql://...@pooler.supabase.com:6543/postgres?pgbouncer=true"
3. 用错端口
- 6543 = 连接池(PgBouncer)
- 5432 = 直连(PostgreSQL)
端口搞反会直接连不上或出现各种奇怪错误。
总结
在 serverless 部署里,连接池不是“可有可无”,而是必需品。不然应用只要有点流量就会崩。
两个 URL 的配置一开始看起来很怪,但它优雅地解决了两个现实问题:
- DATABASE_URL(连接池)高效处理大量并发请求
- DIRECT_URL(直连)确保迁移安全、可控
理解了这个架构背后的原因,配置就会变得理所当然。
这篇文章写于我搭建 Next.js + Prisma + Supabase 项目、纳闷为什么要两个数据库 URL 的时候。
往期回顾
相关文章
2026年1月22日
Vercel AI SDK 沙箱全解析:从 Edge Runtime 到 Firecracker MicroVM
深入解析 Vercel 的 AI 代码执行方案:Edge Runtime 的 V8 隔离、Sandbox 产品的 Firecracker 架构,以及 AI SDK 6 的代码执行工具集成。
2026年2月27日
Claude Code 的记忆机制:从 CLAUDE.md 到 Auto Memory,它到底记住了什么?
Claude Code 刚上线了 Auto Memory 功能。加上原有的 CLAUDE.md 体系,它现在有六层记忆结构。这篇拆解每层的用途、加载时机,以及怎么用才不会变成负担。
2026年2月26日
Nano Banana 2:Pro 级图片生成,Flash 级速度,还便宜了 40%
Google 今天发布 Nano Banana 2(Gemini 3.1 Flash Image),把 Pro 模型的图片质量搬到了 Flash 的速度和价格上。对开发者来说,这可能是目前性价比最高的 AI 图片生成 API。
合作伙伴
CompeteMap — 英国及爱尔兰学生竞赛一站式搜索
数学、编程、科学、写作等各类竞赛信息汇总,支持按年龄和科目筛选,再也不错过报名截止日。