SetConnMaxLifetime是“到期销毁”而非保活,连接创建后超时即关闭,不探测状态;推荐设为30秒至5分钟,且须配合SetConnMaxIdleTime和QueryContext使用。

sql.DB 的 SetConnMaxLifetime 不是保活,是“到期销毁”
很多人以为 SetConnMaxLifetime 是让连接保持活跃,其实它只做一件事:连接从创建起,活满指定时间就强制关掉,下次借的时候必须新建。它不发心跳、不检测连通性、也不重连——纯粹是“用够就扔”。
设为 0(默认)等于放任连接永久存活,极易撞上数据库侧的 wait_timeout(MySQL 默认 8 小时)或中间件(如 SLB/NAT)的静默断连,结果就是偶发 read: connection reset by peer。
推荐值:30 秒~5 分钟。API 类服务建议 30 * time.Second;长事务批处理可放宽到 3 * time.Minute。注意:这个设置只对**已归还到池中**的连接生效,正在执行 Query 或 Exec 的连接不受影响。
SetConnMaxIdleTime 是防空闲断连的关键(Go 1.15+)
SetConnMaxIdleTime 才是真正管“空闲连接能躺多久”的参数。它比老式靠 SetMaxIdleConns + 被动等待更主动,专门应对云环境里 NAT、LB、防火墙在 3~5 分钟后踢掉空闲连接的问题。
常见错误:只配了 SetConnMaxLifetime 却漏掉 SetConnMaxIdleTime,导致连接池里一堆“看着健康、一用就崩”的空闲连接。
实操建议:
- 必须和
SetConnMaxLifetime配合使用,且一般设得更短(例如后者 5 分钟,前者设 2~3 分钟) - 若用的是 Go SetMaxIdleConns + 更激进的
SetConnMaxLifetime(如 60 秒)来缓解 - 不要设为
0,否则空闲连接永不释放,资源滞留风险高
超时必须配合 DSN 和 Context 使用才真正生效
光调池子参数不够。MySQL 驱动要求 DSN 必须带 parseTime=true&loc=Local(或 loc=Asia%2FShanghai),否则时间字段解析错乱,可能间接引发上下文超时误判;而所有查询必须走 QueryContext/ExecContext,传带 timeout 的 context.Context,否则一旦 DB 卡住,goroutine 就永远挂起。
典型错误现象:
- DSN 漏了
parseTime=true→time.Time字段读出来是零值,业务逻辑 fallback 到默认时间,后续 context timeout 判定失准 - 用
db.Query替代db.QueryContext→ 网络卡死时无超时,进程 fd 耗尽,lsof -p $PID | wc -l持续上涨 -
context.WithTimeout设太短(如 100ms),但 SQL 实际耗时 200ms → 频繁触发 cancel,日志刷屏context deadline exceeded
启动时 db.Ping() 只是起点,不是终点
db.Ping() 只验证一个连接是否通,不能代表整个池子健康。它不检查空闲连接是否已失效(比如 MySQL 主动 kill 了 sleep 连接),也不覆盖 DNS、TLS、认证链路的全路径。
线上 readiness probe 建议用真实路径:
- 避免只跑
db.Ping(),改用db.QueryRow("SELECT 1").Scan(&i) - 如果用了 GORM v2,
db.Ping()已弃用,必须用db.Session(&gorm.Session{}).Ping() - 失败必须显式处理(
log.Fatal或 panic),不能忽略——否则服务看似启动成功,首请求才暴露网络不通、密码错、安全组未开等问题
最易被忽略的一点:连接池超时参数再合理,也救不了没提交的事务。只要 *sql.Tx 没调 Commit() 或 Rollback(),那个连接就永远不会归还,db.Stats().OpenConnections 会持续上涨,最终把池子堵死。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











