必须在sql.Open()后立即调用db.Ping()验证连通性,否则首请求必崩;连接池参数需合理配置:SetMaxOpenConns与SetMaxIdleConns须匹配,SetConnMaxLifetime应略小于数据库wait_timeout,事务内严禁混用db.和tx.。

sql.Open() 后不调 db.Ping() 就上线?首请求必崩
sql.Open() 只解析 DSN、初始化 sql.DB 结构体,**根本不建真实连接**。真正拨号发生在第一次 db.Query()、db.Exec() 或 db.Ping()。服务日志显示 “DB initialized” 却卡在第一个 HTTP 请求,报 dial tcp: i/o timeout 或 access denied,基本就是这个原因。
必须紧随 sql.Open() 后立即执行:
if err := db.Ping(); err != nil {
log.Fatal("failed to connect to DB:", err)
}别把 db.Ping() 当健康检查 endpoint 放进 k8s readiness probe——它只测单次连通性,无法反映空闲连接是否已失效。
SetMaxOpenConns 和 SetMaxIdleConns 怎么设才不踩坑
常见现象:QPS 没变但 P95 延迟跳高,db.Stats().WaitCount 持续上升;或数据库直接报 ERROR 1040: Too many connections。
立即学习“go语言免费学习笔记(深入)”;
SetMaxOpenConns 控制“同时最多有多少个活跃连接(含忙 + 空闲)”,SetMaxIdleConns 只管“空闲连接池里最多留几个”。容易踩的坑有:
-
SetMaxIdleConns设得比SetMaxOpenConns大 → Go 1.12+ 直接 panic,报错max idle conns exceeds max open conns -
SetMaxOpenConns设为 0(无限制)→ 打爆 MySQL 默认max_connections=151或 PostgreSQL 的 backend 进程上限 - MySQL 生产常见值:
SetMaxOpenConns = 100~120(≈数据库max_connections× 0.6~0.8),SetMaxIdleConns = 50~60(≈前者 × 0.5,且 ≥5) - PostgreSQL 建议按每核 ≈20 估算
SetMaxOpenConns,SetMaxIdleConns必须 ≤ 前者
事务里混用 db.Query() 和 tx.Query()?连接永远不归还
现象:db.Stats().InUse 持续高位,WaitCount 不涨,数据库连接数缓慢爬升,最终触发 Too many connections。
根本原因:事务内调 db.Query() 会从全局连接池另取一个连接,既不参与当前事务,又占着连接不放;只有 tx.Query() 和 tx.Exec() 复用事务绑定的那条连接。
必须遵守:
- 事务内所有 SQL 必须走
*sql.Tx实例,绝不能穿插db.*()调用 -
sql.Tx必须显式Commit()或Rollback(),漏写等于连接泄漏 - 开启事务必须带 context:
db.BeginTx(ctx, &sql.TxOptions{}),其中ctx建议设 5s 超时
连接池参数没配对 SetConnMaxLifetime?空闲连接突然全失效
MySQL 默认 wait_timeout = 28800(8 小时),PostgreSQL 默认 tcp_keepalives_idle = 7200(2 小时)。若 SetConnMaxLifetime 设为 0 或远大于数据库超时值,空闲连接会在某次复用时突然报 invalid connection 或 connection reset by peer。
正确做法是略小于数据库的 wait_timeout:
- MySQL:设
SetConnMaxLifetime(27 * time.Minute) - PostgreSQL:设
SetConnMaxLifetime(110 * time.Minute) - 该值不是越小越好——太短会导致频繁新建连接,增加握手开销
连接池本身没问题,但生命周期和数据库侧配置脱节,是最隐蔽也最难定位的一类问题。


















