Go 数据库连接池调优:用 sql.DB 的 SetMaxOpenConns 扛住高并发
test12026-09-162 次阅读
一个被忽略的默认行为
Go 标准库 database/sql 的 sql.DB 不是「一条连接」,而是一个连接池。但很多人以为它是单连接,于是在函数里反复 Open,或者从不开连接数,结果线上突发流量时数据库侧连接被打满,应用也开始排队等待。
默认情况下,sql.DB 没有最大连接数上限(MaxOpenConns = 0 表示无限制),MySQL 服务端 max_connections 一旦被突破就会拒绝新连接,故障就此产生。
三个必须设置的参数
- SetMaxOpenConns:池中最多同时打开的连接数,对应数据库并发压力上限。
- SetMaxIdleConns:最大空闲连接数,避免高峰期频繁建连。
- SetConnMaxLifetime:连接最长存活时间,配合数据库或中间件(如云厂商的空闲回收)防止拿到已被服务端关闭的连接。
可运行示例
package main
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func newPool(dsn string) (*sql.DB, error) {
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, err
}
// 关键调优:把池子上限钉死,别让默认无限制冲垮数据库
db.SetMaxOpenConns(50)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(30 * time.Minute)
// 验证真能连上,sql.Open 本身不会建连
if err := db.Ping(); err != nil {
return nil, err
}
return db, nil
}
func main() {
db, err := newPool("user:pass@tcp(127.0.0.1:3306)/app?parseTime=true")
if err != nil {
log.Fatal(err)
}
defer db.Close()
var n int
if err := db.QueryRow("SELECT COUNT(*) FROM orders").Scan(&n); err != nil {
log.Fatal(err)
}
log.Printf("orders = %d", n)
}
参数怎么选
- MaxOpenConns 不要拍脑袋等于数据库 max_connections,要给其他消费者留余量,常见做法是数据库上限的 60% 到 80%。
- MaxIdleConns 小于 MaxOpenConns,否则空闲连接会占满开放连接数,导致峰值拿不到新连接。
- SetConnMaxLifetime 设成比数据库或代理的空闲超时更短,比如云 MySQL 空闲回收通常是几分钟到几十分钟,设 30 分钟比较安全。
监控比设置更重要
db.Stats() 能拿到实时池状态,建议接入指标采集,重点看 InUse(在用连接)是否长期贴近 MaxOpenConns,以及 WaitCount / WaitDuration 是否持续增长——后者意味着已经在排队等连接,说明池子偏小或慢查询拖住了连接。
s := db.Stats()
log.Printf("inUse=%d idle=%d waitCount=%d wait=%v",
s.InUse, s.Idle, s.WaitCount, s.WaitDuration)
T
test1
文章作者
一个被忽略的默认行为 Go 标准库 database/sql 的 sql.DB 不是「一条连接」,而是一个连接池。但很...
- 分类
- 技术
- 发布时间
- 2026-09-16
- 字数
- 约 1560 字
- 阅读
- 2 次
