sql.db.stats() 返回 sql.dbstats 结构体,包含openconnections、inuse、idle、waitcount、waitduration等实时连接池快照字段,其中openconnections == inuse + idle,且inuse不等于并发请求数。

Go 的 sql.DB.Stats() 返回什么数据
sql.DB.Stats() 返回的是 sql.DBStats 结构体,它反映的是连接池当前的实时快照,不是历史聚合值。关键字段包括:OpenConnections(已打开的连接数)、InUse(正被 query 或 exec 占用的连接)、Idle(空闲连接数)、WaitCount(因连接不足而阻塞等待的总次数)、WaitDuration(累计等待时长)。注意:OpenConnections == InUse + Idle,但 InUse 并不等于并发请求数——因为事务或长时间未 Close 的 *sql.Rows 也会占着连接不放。
导出 DBStats 到 Prometheus 的常见错误
直接在 HTTP handler 里调用 db.Stats() 并返回指标,容易踩两个坑:一是没加锁(Stats() 内部是原子读,安全);二是误把瞬时值当速率用,比如对 WaitCount 做 rate() 计算——这是错的,WaitCount 是单调递增计数器,Prometheus 客户端应使用 prometheus.NewCounterFunc 包装,而不是 Gauge。
- 用
prometheus.NewCounterFunc暴露WaitCount和MaxOpenConnections这类只增或固定值 - 用
prometheus.NewGaugeFunc暴露OpenConnections、InUse、Idle这类可升可降的瞬时状态 - 避免每秒调用
db.Stats()多次——它轻量,但高频采集无意义;10s 间隔足够
为什么 MaxIdleConns 设太小会导致 WaitCount 突增
MaxIdleConns 控制空闲连接上限,但它和连接复用效率强相关。如果设为 0 或过小(如 2),高并发下空闲连接很快被回收,新请求只能新建连接或排队等——哪怕 MaxOpenConnections 还有余量,只要没有空闲连接可用,就会触发等待逻辑,WaitCount 就会涨。典型现象是 p95 延迟毛刺 + WaitDuration 上升。
- 生产建议:设
MaxIdleConns == MaxOpenConnections,除非你明确要限制空闲资源 - 注意
SetConnMaxLifetime和SetConnMaxIdleTime会主动 kill 连接,可能加剧空闲连接流失 - 验证方式:压测时观察
Idle是否长期趋近于 0,同时WaitCount持续增加
DBStats 不显示连接泄漏,但能帮你定位
db.Stats() 本身不报错,也不告警,但它暴露的几个数字组合起来就是泄漏信号:InUse 持续增长不回落、Idle 趋近于 0、OpenConnections 逼近 MaxOpenConnections —— 这大概率说明有 *sql.Rows 没 Close,或事务没 Commit/Rollback。尤其注意 Rows.Close() 必须显式调用,defer 不可靠(比如函数提前 return)。
立即学习“go语言免费学习笔记(深入)”;
- 临时诊断:在 panic hook 或健康检查 endpoint 打印
db.Stats(),看是否随请求累积上涨 - 更准的方式:结合
runtime.SetFinalizer给*sql.Rows加日志(仅开发环境),或用go tool trace抓 goroutine 阻塞点 - 别依赖
MaxLifetime当兜底——它只管连接年龄,不管业务逻辑是否卡住连接
InUse 和业务路径对上,搞清哪个接口、哪段 SQL、哪种参数组合在持续 hold 住连接。










