慢连接通常是go应用连接池阻塞而非sql慢,通过db.stats()查看inuse、waitcount、idle三字段可精准定位:inuse接近maxopenconnections且waitcount>0说明排队等连接,idle=0但inuse

慢连接不是数据库卡了,而是你的 Go 应用在等连接——先查 db.Stats(),再看 WaitCount 和 WaitDuration 是否非零。
怎么一眼看出是连接池堵了而不是 SQL 慢
执行 db.Stats() 后重点关注三个字段:
-
InUse接近或等于MaxOpenConnections:说明连接全被占着,新请求开始排队 -
WaitCount > 0且WaitDuration持续增长:证明有请求卡在「等连接」阶段,不是 SQL 执行慢 -
Idle == 0但InUse :可能是连接泄漏(比如没调 <code>rows.Close()),空闲连接没被归还
如果 WaitCount 为 0,但查询延迟高,那问题大概率在 SQL 本身或网络,该去看 EXPLAIN 或慢日志。
SetMaxOpenConns 设多少才不拖后腿
这个值不是拍脑袋定的,它直接决定你最多能并发跑几个查询。设小了会排队,设大了可能压垮 DB 或触发锁竞争:
- 粗略公式:
SetMaxOpenConns = ceil(峰值 QPS × 平均查询耗时秒数)。例如峰值 120 QPS、平均查一次 150ms →120 × 0.15 = 18 → 设 20 - 别盲目对标 DB 的
max_connections。MySQL 默认 151,但你的服务通常只配 20–50,留余量给其他服务或连接监控 - 上线后观察
WaitCount:持续上升就加,加完仍高就查是不是有长事务或泄漏;加太大后 P99 反而升高,说明 DB 端已成瓶颈
为什么设置了 SetMaxIdleConns 还是频繁建连
SetMaxIdleConns 控制的是「用完归还后能留几个不关」,但它不会主动预热连接。以下情况会导致空闲连接快速清零:
- 没设
SetConnMaxLifetime:DB 主动断连后,Go 不知道连接失效,下次复用时报错,然后丢弃并新建 —— 表现就是Idle归零、OpenConnections波动大 -
SetMaxIdleConns设得太小(比如 1):一个连接刚归还,下一个请求立刻拿走,没机会积累空闲数 - 连接池里全是“老化”连接(超
ConnMaxLifetime),被后台 goroutine 清理,导致空闲池真空
建议组合配置:SetMaxIdleConns(10) + SetConnMaxLifetime(30 * time.Minute),既保复用又防僵死。
事务里忘了 Commit/Rollback 是最隐蔽的连接杀手
事务开启后,连接就被绑定在 *sql.Tx 上,直到显式 Commit() 或 Rollback() 才释放回池。漏掉任何一个,连接就永远卡住:
- 现象:某接口偶发超时,
db.Stats().InUse持续缓慢上涨,WaitCount突增,重启后恢复 - 根本原因:
if err != nil { return }后直接返回,没写tx.Rollback();或者defer tx.Commit()被 panic 绕过 - 安全写法:用
defer func()匿名函数兜底,检查tx是否已提交/回滚,未处理则强制Rollback()
连接池不报错、不告警,只默默让后续所有请求排队等待 —— 这是线上最常被忽略、排查成本最高的点。











