Go中数据库连接需四步:配驱动、写DSN、调连接池参数(SetMaxOpenConns≤SetMaxIdleConns,否则panic)、验连通性(db.PingContext);sql.Open仅校验DSN格式,真建连在首次Query/Exec时。

Go 里没有“设置数据库”这回事,只有配驱动、写 DSN、调连接池参数、验连通性这四步。漏掉任何一步,sql.Open 都会静默成功,但第一个 db.Query 就报错。
sql.Open 后为什么没报错,但查数据时 panic?
sql.Open 只校验 DSN 格式和驱动注册状态,不发任何网络请求。真正建连发生在第一次 db.Ping()、db.Query() 或 db.Exec() 时。
- 常见误判:看到
err == nil就以为连上了,结果服务启动后第一个请求直接dial tcp: i/o timeout - 必须在初始化后加健康检查:
if err := db.PingContext(context.WithTimeout(context.Background(), 5*time.Second)); err != nil { log.Fatal("db connect failed:", err) } - 容器部署时注意启动顺序:DB 容器可能比 Go 服务慢几秒就绪,
PingContext要带重试逻辑(比如循环 3 次,每次间隔 1s)
MySQL 和 PostgreSQL 的 DSN 格式差异
DSN 不是标准格式,每个驱动自己定义。写错一个字符(比如把 & 写成 &),sql.Open 不报错,但后续所有操作都失败。
- MySQL(用
github.com/go-sql-driver/mysql):user:pass@tcp(127.0.0.1:3306)/dbname?parseTime=true&loc=Local注意:parseTime=true和loc=Local必须成对出现,否则time.Time字段读出来是零值或 panic - PostgreSQL(用
github.com/lib/pq):host=localhost port=5432 user=myuser password=mypass dbname=mydb sslmode=disable注意:漏掉sslmode=disable会导致连接卡死(默认尝试 SSL,但本地开发通常没配) - SQLite:
./app.db?_busy_timeout=5000注意:路径含空格必须加引号,否则解析失败
SetMaxOpenConns 和 SetMaxIdleConns 容易 panic
这两个参数不是“建议配置”,而是运行时强约束。设错会直接 crash,不是 warning。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
SetMaxIdleConns(n)必须 ≤SetMaxOpenConns(n),否则 Go 1.12+ 会 panic:max idle conns exceeds max open conns - 只设
SetMaxIdleConns(10)没设SetMaxOpenConns→ 默认MaxOpenConns = 0(无限制),等于放弃兜底,线上高并发大概率触发ERROR 1040: Too many connections - 稳妥做法:
db.SetMaxOpenConns(20) db.SetMaxIdleConns(10)
生产环境建议MaxIdleConns ≈ MaxOpenConns × 0.5,留出并发余量
SetConnMaxLifetime 不是保活,是强制换新
这个参数常被误解为“心跳续命”。它实际作用是:连接从创建起,最多活这么长时间,到期就关掉,下次需要时新建一个。
- MySQL 默认
wait_timeout = 28800(8 小时),但云数据库(如 RDS)常缩到 30 分钟;设SetConnMaxLifetime(25 * time.Minute)才能避开被 DB 主动 kill - 设成
0表示永不过期(不推荐);设太短(如1 * time.Second)会导致频繁握手,压测时 CPU 明显升高 - Go 1.15+ 还要配
SetConnMaxIdleTime(30 * time.Minute):控制空闲连接最长待多久,避免池子里堆满僵尸连接
最常被忽略的点:所有查询必须用 db.QueryContext / db.ExecContext,传入带超时的 context。裸调 db.Query 一旦数据库响应慢,goroutine 就卡死,积压后直接 OOM。

















