GORM 不自动重连,db.Ping() 仅检测空闲连接存活且不保证新连接可靠;需显式配置 sql.DB 连接池参数、使用 context 超时、分层错误处理并重建 *gorm.DB 实例。

连接断开后 GORM 不会自动重连,db 实例本身仍可用,但后续操作会持续报错(如 dial tcp: i/o timeout 或 connection refused),必须主动干预。
为什么 db.Ping() 返回 nil 也不代表连接可靠
db.Ping() 只检测当前连接池中某个空闲连接是否存活,无法反映新连接能否建立;若数据库已重启、网络中断或防火墙拦截,首次获取新连接时仍会失败。更关键的是:GORM 的 *gorm.DB 是无状态句柄,它不维护“连接是否就绪”的上下文。
- 调用
db.Ping()前应先确保db非 nil,否则 panic - 即使
db.Ping()成功,下一次Create()仍可能因连接池耗尽或 DNS 失效而失败 - 推荐搭配
context.WithTimeout使用,避免阻塞在连接建立阶段
连接池参数没设对,比断连更隐蔽
默认连接池最大打开数是 0(不限制),但 MySQL 服务端通常有 max_connections 上限。当应用并发突增,大量连接堆积又未及时释放,会导致新连接被拒绝——现象和“断连”一致,实则是资源耗尽。
-
sqlDB.SetMaxOpenConns(n)必须显式设置,建议 ≤ 数据库侧max_connections * 0.8 -
sqlDB.SetMaxIdleConns(m)推荐设为与MaxOpenConns相同或略小,避免空闲连接长期占用 -
sqlDB.SetConnMaxLifetime(1h)强制复用旧连接前重建,规避 MySQL 的wait_timeout自动断连
重连逻辑不能写在业务函数里
每次查询都包裹 if err != nil { reinitDB() } 是反模式:既破坏事务一致性,又掩盖真实错误类型(比如是 SQL 写错还是连不上)。GORM 没有内置重连机制,正确做法是分层处理:
- 启动时做一次
db.Ping()+db.Migrator().CurrentDatabase(),失败则 abort - 运行中遇到
driver.ErrBadConn或net.OpError类错误,才触发连接重建(需重建整个*gorm.DB实例) - 所有业务代码只依赖一个全局
*gorm.DB,不要在子函数里重新gorm.Open - HTTP handler 中可结合中间件,在请求入口统一捕获连接类错误并返回 503
MySQL 服务端 kill 连接后,GORM 会卡住几秒才报错
这是底层 database/sql 的行为:连接被服务端强制关闭后,客户端发包会等到 TCP 超时(默认几十秒),期间所有调用阻塞。必须靠连接池参数和上下文超时来兜底。
- 在
gorm.Config中启用PrepareStmt: true,减少预编译语句反复创建开销 - 所有数据库操作必须传入带 timeout 的
context.Context,例如db.WithContext(ctx).First(&u) - 检查 MySQL 的
wait_timeout和interactive_timeout,建议设为 300 秒,并匹配ConnMaxLifetime
最易被忽略的点:连接池参数是在 *sql.DB 层配置的,不是 *gorm.DB;很多人只改了 gorm.Open 的选项,却忘了调用 db.DB().SetMaxOpenConns(...) —— 这一步漏掉,等于没配。


















