maxopenconns应根据数据库max_connections和服务实例数合理分摊,避免过高导致too many connections或过低限制qps;需监控db.stats()中waitcount和waitduration,非零即表明连接池成瓶颈。

MaxOpenConns 设多少才不拖垮数据库
设太高,数据库直接报 too many connections;设太低,应用排队等连接,QPS 上不去。关键不是“推荐值”,而是看数据库的 max_connections 和你的服务并发模型。
- 先查数据库侧上限:PostgreSQL 用
SHOW max_connections,MySQL 用SHOW VARIABLES LIKE 'max_connections',别盲目设到 1000+ - 按服务实例数分摊:比如 DB 允许 300 连接,部署 3 个 Go 服务实例,每个
MaxOpenConns别超过 80–100(留余量给其他客户端) - 上线后盯
pg_stat_activity或SHOW PROCESSLIST,如果长期接近上限,说明不是调大,而是得查有没有连接没释放(比如 defer db.Close() 写错位置、或忘了用db.QueryRow而用了db.Query却没rows.Close())
MaxIdleConns 和 MaxIdleConnsPerHost 的区别与误用
MaxIdleConns 是整个 *sql.DB 实例的空闲连接总数上限;MaxIdleConnsPerHost 是 net/http 的字段,和数据库池无关——这是最常见的混淆点,Go 的 database/sql 根本不认后者。
-
MaxIdleConns建议设成和MaxOpenConns相同或略低(比如 90%),避免空闲连接占着坑却不干活 - 如果设为 0,每次查询都新建连接再关,性能灾难;如果设得比
MaxOpenConns大,实际无效,sql.DB会自动截断 - 注意:空闲连接超时由
SetConnMaxIdleTime控制,不是靠 GC 或连接池自动清理,不设就一直挂着(尤其在低流量时段)
连接泄漏的典型现象和快速定位法
表现不是报错,而是 netstat -an | grep :5432 | wc -l(或对应 DB 端口)持续上涨,且 pg_stat_activity 里大量状态为 idle 的连接,时间戳几小时没变。
- 最常见原因:用
db.Query后没调rows.Close();db.QueryRow不需要 Close,但Query必须 - 检查所有
defer rows.Close()是否写在if err != nil分支外——写在里面等于根本没执行 - 临时加监控:在
db.SetMaxOpenConns后立刻打日志,再定期 log.Printf("open: %d, idle: %d", db.Stats().OpenConnections, db.Stats().Idle)
高并发短连接场景下要不要调小 ConnMaxLifetime
要,但不是为了“让连接更健康”,而是防止连接池在数据库主从切换、连接被中间件(如 pgbouncer)踢掉后,还拿着已失效的连接继续发请求,结果卡住几秒才超时。
立即学习“go语言免费学习笔记(深入)”;
-
ConnMaxLifetime设 5–15 分钟足够,太长(如 1 小时)会让失效连接滞留太久;太短(如 30 秒)则频繁新建销毁,增加握手开销 - 它和
SetConnMaxIdleTime配合用效果最好:前者防“老而病”,后者防“闲而占” - 注意:这个值是连接从创建起的总存活时间,不是空闲时间,别和
MaxIdleConns搞混
真正难的不是算数字,是上线后盯着 db.Stats() 里的 WaitCount 和 WaitDuration ——只要这两个非零,说明连接池已经成了瓶颈,参数就得动。










