SetMaxOpenConns应设为数据库max_connections的60%~80%,如MySQL默认151则建议100左右;设为0会突破DB限制引发“Too many connections”,必须显式配置并配合SetMaxIdleConns和SetConnMaxLifetime协同调优。

Go 的 database/sql 连接池不是“开箱即用就稳”,默认配置(MaxOpenConns=0、MaxIdleConns=2)在生产环境几乎必然翻车。必须显式调优,且关键参数之间存在强耦合——单改一个,其他不跟上,等于白调。
SetMaxOpenConns 设多少才不压垮数据库又不卡请求
它不是 QPS × 耗时的简单乘积,而是受数据库服务端资源硬约束的并发上限。盲目设高会触发 MySQL/PostgreSQL 主动拒绝连接或引发大量空闲线程争抢 CPU。
- 先查数据库真实上限:
SHOW VARIABLES LIKE 'max_connections';(MySQL)或SELECT setting FROM pg_settings WHERE name = 'max_connections';(PostgreSQL) - 线上建议值 = 数据库
max_connections的 60%~80%,留余量给备份、监控、其他微服务 - 观察
db.Stats().OpenConnections:长期 > 90%MaxOpenConns→ 加;长期 - 别信“理论并发数”:300 QPS × 20ms = 6 并发?实际要乘安全系数 1.5~2,再向上取整
SetMaxIdleConns 和 SetConnMaxIdleTime 必须配对生效
只设 SetMaxIdleConns 不设 SetConnMaxIdleTime,空闲连接会一直堆着,直到数据库侧超时断连(如 MySQL wait_timeout=300),之后 Go 还拿它去发请求,直接报 ERROR 2013: Lost connection。
-
SetMaxIdleConns建议为SetMaxOpenConns的 1/2~1/3(例如 Open=100 → Idle=30~50) -
SetConnMaxIdleTime必须 小于 数据库wait_timeout(如 DB 是 300s,这里设 240s),且推荐云环境(RDS/PolarDB)缩到5 * time.Minute - 注意:
SetConnMaxIdleTime仅 Go 1.15+ 支持,低版本只能靠SetConnMaxLifetime间接控制
事务内混用 db.Query 和 tx.Query 会导致连接泄漏
事务里调 db.Query() 会从全局池另取连接,既不参与事务,又占坑不放——此时 db.Stats().InUse 持续高位但 WaitCount 不涨,排查极难。
立即学习“go语言免费学习笔记(深入)”;
- 所有事务内 SQL 必须统一走
tx.Query()、tx.Exec(),禁用db.Query() - 开启事务必须带 context 超时:
db.BeginTx(ctx, &sql.TxOptions{}),其中ctx建议设 5s -
*sql.Tx对象必须显式Commit()或Rollback(),否则连接永不归还 - 漏写
rows.Close()同样泄漏:用defer rows.Close()是底线,不是可选项
db.Stats() 是唯一可信的实时体检报告
别依赖 db.Ping() 判断健康——它只测“门铃响不响”,不反映池内连接是否真实可用、是否被卡死、是否在排队。
- 重点关注三个动态指标趋势:
WaitCount(排队次数)、InUse(当前占用)、MaxIdleClosed(因空闲被主动关闭的连接数) -
WaitCount持续上升 → 连接不够,优先调SetMaxOpenConns -
InUse == MaxOpenConns且长期不降 → 连接没归还,查事务/rows 是否漏处理 -
MaxIdleClosed > 0频繁增长 →SetMaxIdleConns太高 或SetConnMaxIdleTime太短 - 把
/db/stats端点接入 Prometheus,对OpenConnections / MaxOpenConns > 0.9设 1 分钟持续告警
最常被忽略的是:连接池参数调完不监控,等于没调;事务忘记 commit,等于埋定时泄漏;空闲连接不设回收时间,等于养了一池子僵尸连接。这些点不落地,性能优化就是纸上谈兵。



















