是设计,不是 bug:db.query() 在连接池满时会阻塞等待,属连接池守门机制;需通过连接获取超时、空闲/生命周期控制及非阻塞降级实现可控、可观察、可兜底。

连接池满了,db.Query() 就卡住?这是设计,不是 bug
Go 的 *sql.DB 默认行为就是:当所有连接都在用、且已达 SetMaxOpenConns 上限时,后续调用 db.Query()、db.Exec() 等会**阻塞等待**,直到有连接被归还。这不是故障,而是连接池的守门机制。
但线上一旦卡住,HTTP 请求就 hang 住,超时、重试、雪崩全跟着来。所以关键不是“怎么不让它卡”,而是“怎么让卡得可控、可观察、可兜底”。
- 别依赖
context.WithTimeout包一层就以为万事大吉——它只能中断 SQL 执行,但连接本身仍被占用,直到事务结束或连接归还 - 真正有效的超时必须落在连接获取环节:用
db.SetConnMaxIdleTime和db.SetConnMaxLifetime配合数据库侧超时(如 MySQLwait_timeout),避免“僵尸连接”占着茅坑 - 如果业务允许降级(比如非核心日志写入),可用
select+default实现非阻塞提交:select { case
db.Stats() 是唯一能信的“连接池体检报告”
只靠 db.Ping() 判断健康,等于只敲了下门就宣布房子没问题。真实水位看 db.Stats() 返回的结构体。
-
OpenConnections持续 ≥SetMaxOpenConns—— 连接池已满,要么扩容,要么查慢 SQL 占着不放 -
InUse长期高、Idle长期低 —— 连接复用率差,可能是事务没提交、*sql.Tx忘记Commit()或Rollback() -
WaitCount和WaitDuration持续上涨 —— 请求在排队,不是连接不够,就是单次查询太慢拖住了连接 -
MaxIdleClosed或MaxLifetimeClosed高频增加 —— 空闲/存活时间设得太短,连接频繁重建,容易触发read: connection reset by peer
SetMaxOpenConns 设多少才不翻车
硬算 QPS × 平均耗时 = 理论并发数?线上基本不准。MySQL 默认 max_connections=151,你设 db.SetMaxOpenConns(200),第一波高峰就收数据库拒绝连接错误。
- 先查数据库真实上限:
SHOW VARIABLES LIKE 'max_connections'(MySQL)或SELECT setting FROM pg_settings WHERE name = 'max_connections'(PostgreSQL) - 生产推荐值:数据库上限的 60%~80%,留余量给备份、监控、其他服务
- 起步建议:QPS 100 以下的服务,
SetMaxOpenConns=20~50;中高流量服务,从 80 开始压测,观察db.Stats().WaitCount - 别只调
MaxOpenConns:它必须和SetMaxIdleConns(建议 1/2~1/3)、SetConnMaxLifetime(略小于 DB wait_timeout)、SetConnMaxIdleTime(5 分钟起)一起调
事务不提交,连接就永远卡住
*sql.Tx 不是连接池的一部分,它是独占一个连接的“租约”。只要没调 tx.Commit() 或 tx.Rollback(),那个连接就不会归还池子,InUse 就一直涨。
- 常见陷阱:在
defer tx.Rollback()后忘了删掉,结果成功路径也 rollback 了 - 更安全写法:
tx, err := db.Begin() if err != nil { return err } defer func() { if r := recover(); r != nil { tx.Rollback() panic(r) } }() // ... do work if err := tx.Commit(); err != nil { tx.Rollback() return err } - 加个超时兜底:启动 goroutine,在
time.AfterFunc(30*time.Second, func(){ tx.Rollback() }),防死锁
连接池参数不是设完就一劳永逸的——它得跟着数据库负载、网络环境、SQL 效率动态呼吸。最常被忽略的一点:每次上线新 SQL,都要重新看一眼 db.Stats() 里的 InUse 和 WaitDuration,而不是等告警响了才去翻日志。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











