Go 的 sql.DB 本质是连接池,需严格配置 SetMaxOpenConns 和 SetMaxIdleConns(后者≤前者),并立即调用 db.Ping() 验证;SetConnMaxLifetime 应设为30s~5分钟以主动轮换连接;所有操作须用 Context 方法,db 作为全局变量生命周期与进程一致,DSN 需加 parseTime=true&loc=Local。

Go 的 sql.DB 本身就是连接池,不是“要不要配”的问题,而是“配错就炸”——设错 SetMaxOpenConns 和 SetMaxIdleConns 会直接 panic 或静默截断;不调 db.Ping(),服务启动时不报错,第一个请求才挂。
SetMaxOpenConns 和 SetMaxIdleConns 必须满足大小关系
这两个值不是独立配置的,违反大小约束会立即崩溃或失效:
-
SetMaxIdleConns(10)但SetMaxOpenConns(5)→ 运行时 panic,错误是"max idle conns exceeds max open conns" -
SetMaxIdleConns(20)但SetMaxOpenConns(50)→ 合法,空闲连接最多留 20 个 - 只设
SetMaxIdleConns(10)没设SetMaxOpenConns→ 默认MaxOpenConns = 0(无限制),极危险,可能打爆数据库 - 生产环境建议:
SetMaxOpenConns≤ 数据库max_connections× 0.8(如 MySQL 默认 151,设 120 就已偏高);SetMaxIdleConns设为前者的 1/2~2/3(如 120 → 60)
SetConnMaxLifetime 不是保活,是主动换新
它不会发心跳、不检测连接状态,只是强制连接用满指定时间后关闭,下次再借就是新连接:
- 设为
0表示永不过期 → 容易遇到i/o timeout或connection reset by peer(尤其在 RDS、ProxySQL、SLB 后) - 设太短(如
1 * time.Second)→ 频繁握手,CPU 和延迟双升 - 推荐值:30 秒~5 分钟。API 类服务用 30s,批处理长事务可放宽到 3~5 分钟
- 注意:它只对**已归还到池中**的连接生效,正在执行
Query或Exec的连接不受影响
sql.Open 后必须立刻 db.Ping(),且不能只靠它做健康检查
sql.Open 只解析 DSN、注册驱动,不建真实连接;db.Ping() 才真正拨号并验证连通性:
立即学习“go语言免费学习笔记(深入)”;
- 漏掉
db.Ping()→ DSN 写错、DB 未启动、网络不通等问题全被掩盖,直到第一个业务查询才暴露,日志里全是context deadline exceeded - 但
db.Ping()成功 ≠ 连接池健康:它只测一个连接,不校验空闲连接是否已失效(比如 DB 主动 kill 了 sleep 连接) - 线上 readiness probe 别只用
db.Ping(),应模拟真实路径,例如db.QueryRow("SELECT 1").Scan(&i) - 所有查询必须用
QueryContext/ExecContext,传带 timeout 的context.Context,否则卡死的连接永不归还
别在 handler 或循环里 sql.Open,也别 defer db.Close() 在初始化函数里
*sql.DB 是线程安全的全局资源,生命周期应与进程一致:
- 每次 HTTP 请求都
sql.Open→ 连接池泄漏,DB 很快报Too many connections - 把
db.Close()放在 init 函数里defer→ 初始化完立刻关池,后续所有操作都报database is closed - 正确做法:声明为包级变量,
main函数退出前显式db.Close(),或监听os.Interrupt做优雅关闭 - MySQL 驱动还要求 DSN 加
parseTime=true&loc=Local(或loc=Asia%2FShanghai),否则time.Time字段解析错乱


















