database/sql 自带连接池,sql.DB 即池化句柄;sql.Open 不建连,首次 Query/Exec 才拨号;应合理配置 SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime;超时须用 context.Context 控制;Query 需手动 rows.Close,QueryRow 系列自动关闭。

database/sql 自带连接池,不需要额外引入库
Go 标准库 database/sql 从设计上就是基于连接池的,你调用 sql.Open 得到的 *sql.DB 本身就是一个池化句柄,不是单个连接。常见误解是“打开就连接”,其实 sql.Open 只做驱动注册和配置解析,真正首次建连发生在第一次执行 Query、Exec 等操作时。
所以别在每次查询前 sql.Open,也别手动 defer db.Close()(除非你真要彻底关闭整个池)。
关键参数:SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime
这三个方法控制池行为,直接影响并发吞吐和连接复用率:
-
db.SetMaxOpenConns(n):限制池中**最大同时打开的连接数**。设为 0 表示无限制(危险,可能打爆数据库)。建议设为略高于应用峰值 QPS × 平均查询耗时(秒),例如 QPS=100、平均耗时 50ms → 初值可设 10~15 -
db.SetMaxIdleConns(n):控制**空闲连接上限**。设太小(如默认 2)会导致频繁建连/销毁;设太大浪费资源。通常设为SetMaxOpenConns的 1/2~2/3,且不低于 5 -
db.SetConnMaxLifetime(d):强制连接在池中存活不超过该时长(如30 * time.Minute)。避免因数据库侧连接超时(如 MySQL wait_timeout)导致后续请求报invalid connection
注意 driver 层的 DSN 特性,比如 MySQL 的 timeout 参数不生效
很多开发者在 DSN 里写 timeout=5s&readTimeout=5s&writeTimeout=5s,以为能兜底网络异常,但实际这些参数只影响**单次 dial 或读写操作**,并不影响连接池行为。更关键的是:
立即学习“go语言免费学习笔记(深入)”;
- MySQL 驱动(如
go-sql-driver/mysql)的timeout是 dial timeout,readTimeout仅对单次Read生效,无法中断一个卡住的 long-running 查询 - PostgreSQL 驱动(
lib/pq或jackc/pgx)需用pgx.Config.RuntimeParams或上下文超时配合context.WithTimeout控制查询生命周期 - 真正可靠的超时应放在业务层:所有
db.QueryContext、db.ExecContext必须传入带 timeout 的context.Context
连接泄漏的典型表现与排查方式
现象是 QPS 没涨,但数据库连接数持续攀升,直到触发 too many connections。根本原因常是没正确释放结果集或没关闭 *sql.Rows:
- 用
rows, err := db.Query(...)后,必须显式rows.Close(),哪怕用defer也要确保它在函数退出前执行(注意作用域) - 用
db.QueryRow(...).Scan(...)是安全的,内部自动 Close;但db.QueryRowContext(...)同理,无需额外处理 - 检查
db.Stats()返回的sql.DBStats,重点关注OpenConnections和InUse是否长期不降,或WaitCount持续增长(说明协程在等连接)
连接池本身不会帮你 catch panic 后的 rows.Close,所以涉及 defer 的地方务必确认 panic 路径是否仍会执行。









