验证连接池复用需观察底层连接生命周期,而非仅 db.ping();推荐用 db.stats() 对比并发查询前后 idle 和 inuse 值变化,或启用驱动日志、包装 net.conn 打点。

Go 的 database/sql 自带连接池,测试重点不是“能不能连上”,而是验证池行为是否符合预期——比如并发时是否复用连接、空闲连接是否及时释放、超时是否生效。
如何验证连接池是否真正复用连接
很多测试只检查 db.Ping() 是否成功,这其实绕过了连接池逻辑。真正要观察的是底层连接的生命周期。
- 启用
sql.DB的日志(如用github.com/mattn/go-sqlite3可加_ "github.com/mattn/go-sqlite3"并设置SQLITE_ENABLE_LOG=1),或在驱动层打点(例如包装net.Conn实现) - 更轻量的方法:用
db.Stats()对比两次并发查询前后的Idle和InUse值变化 - 示例:
go fmt.Printf("before: %+v\n", db.Stats()) for i := 0; i < 5; i++ { go func() { db.QueryRow("SELECT 1") }() } time.Sleep(100 * time.Millisecond) fmt.Printf("after: %+v\n", db.Stats()) // 若 Idle 减少、InUse 增加,说明连接被借出;之后 Idle 恢复,说明被归还
SetMaxOpenConns 和 SetMaxIdleConns 的实际影响
这两个参数不光控制数量,更直接影响阻塞行为和资源泄漏风险。
-
SetMaxOpenConns(5):超过 5 个并发Query会阻塞,直到有连接归还(注意:不是报错) -
SetMaxIdleConns(2):空闲连接最多保留 2 个,多余连接会在空闲时被主动关闭(调用Close()) - 常见误配:
SetMaxOpenConns(0)表示无限制,高并发下可能耗尽数据库连接数;SetMaxIdleConns(0)导致每次查询都新建连接、几乎不复用 - 建议组合:
SetMaxOpenConns(20)+SetMaxIdleConns(10)+SetConnMaxLifetime(30 * time.Minute),避免长连接僵死
如何模拟连接泄漏并定位问题
连接泄漏通常表现为 db.Stats().InUse 持续增长、Idle 趋近于 0,且不随请求结束下降。
立即学习“go语言免费学习笔记(深入)”;
- 最常见原因:忘记调用
rows.Close()或tx.Rollback()/Commit() - 测试时可故意注释掉
rows.Close(),再用db.Stats()观察 30 秒内InUse是否只增不减 - 用
pprof辅助:启动net/http/pprof,访问/debug/pprof/goroutine?debug=2查看是否有大量阻塞在driverConn.waitClosed的 goroutine - 对
*sql.Tx尤其要小心:未提交/回滚的事务会一直占用连接,且不会被SetConnMaxLifetime清理
连接池行为高度依赖驱动实现,比如 pgx/v5 的原生连接池比 lib/pq 更激进地复用;而 SQLite 驱动默认不支持并发连接,SetMaxOpenConns 在它身上意义不大。测之前,先确认你用的驱动是否真走 database/sql 标准池路径。










