生产环境必须使用 pgxpool.pool,因其提供连接复用、健康检查、空闲回收及 pgx 全部原生能力;pgx.conn 无复用,sql.db 底层若非 pgxpool 则丢失类型支持与二进制协议;官方明确标注 pgxpool 为 production-ready。

生产环境必须用 pgxpool.Pool,不是 pgx.Conn 或 sql.Open 后直接用 *sql.DB —— 否则高并发下会快速耗尽连接或 DNS 解析失败。
为什么 pgxpool.Pool 是唯一靠谱的选择
PostgreSQL 连接是昂贵资源,pgx.Conn 是单连接、无复用、不自动重连的裸连接;sql.Open("pgx", dsn) 返回的 *sql.DB 虽有连接池,但底层驱动若没走 pgxpool,就无法利用 pgx 的原生类型支持(如 jsonb、uuid、数组)和高效二进制协议。
真正该用的是 pgxpool.Pool:它既提供连接复用、健康检查、空闲连接回收,又保留 pgx 全部能力。官方文档明确标注 pgxpool 是“production-ready connection pool”。
- 别写
pgx.Connect(context.Background(), dsn)—— 这是开发调试用的,上线即崩 - 别依赖
database/sql+lib/pq—— 该驱动已归档,不维护,sslmode=disable在生产等于裸奔 - 初始化后必须立刻
pool.Ping(context.Background()),否则pool.Query第一次调用才暴露连接失败,错误位置难定位
DSN 字符串怎么写才安全可靠
用 URL 格式而非键值拼接,避免密码泄露到日志或监控系统中。PostgreSQL 官方推荐格式:postgres://user:pass@host:port/dbname?sslmode=require&pool_max_conns=20。
-
sslmode=require是底线,本地开发可临时用sslmode=disable,但 CI/CD 流水线或 k8s 部署时必须校验此项 -
pool_max_conns建议设为 10–30(取决于 DB 规格),别盲目设成 100+ —— PostgreSQL 默认max_connections通常只有 100,多个服务共用时极易打满 - host 名必须是容器名(如
pg-test)或 DNS 可解析域名,别写localhost—— Docker 网络里localhost指向容器自身,不是数据库容器 - 如果用环境变量注入,确保
os.Getenv("DB_DSN")不为空,且包含完整参数,缺失?sslmode=...会导致连接静默失败
Gin 中如何把数据库实例注入到 handler
别在每个 handler 里重复 pool.Acquire(),更别全局声明 var DB *pgxpool.Pool 后直接用 —— Gin 的中间件机制更适合做依赖注入。
- 在
main.go初始化好pool后,用gin.Set("db", pool)挂载到引擎实例 - handler 内通过
c.MustGet("db").(*pgxpool.Pool)获取,比传参或全局变量更可控 - 若用 GORM,也应让 GORM 实例从
pgxpool.Pool构建:传gorm.Config{ConnPool: pool},而非再开一套连接池 - 事务操作必须显式调用
pool.Begin(),拿到*pgx.Tx后,所有Query/Exec都走它,严禁混用pool和tx
NULL 值处理不当会导致 panic
PostgreSQL 的 NULL 到 Go 的零值映射是陷阱重灾区。比如字段定义为 email TEXT NULL,结构体字段写成 Email string,rows.Scan(&u.Email) 会直接 panic:sql: Scan error on column index X: unsupported Scan, storing driver.Value type <nil> into type *string</nil>。
- 所有可能为 NULL 的列,对应 Go 字段必须是指针类型(
*string、*int64)或sql.NullXXX类型(sql.NullString、sql.NullInt64) - 用
pgx时优先选指针 —— 它对jsonb、uuid等扩展类型支持更原生,sql.NullXXX只能覆盖基础类型 - JSONB 字段建议用
json.RawMessage或自定义 struct,别用map[string]interface{}—— 反序列化失败时难以定位字段 - GIN 路由里接收参数再写入 DB 前,务必检查是否为 nil,否则
INSERT INTO ... VALUES ($1)传nil会触发驱动 panic,不是 SQL 错误
最常被跳过的一步是连接池 Ping 后加重试逻辑 —— Kubernetes 启动时 DNS 往往延迟就绪,只做一次 pool.Ping 失败就 panic,不如加个最多 3 次、间隔 1 秒的简单循环,比等 readiness probe 更快进入服务状态。











