GORM本身不提供断线重连API,依赖database/sql连接池自动管理;需通过驱动错误码(如pq.Error.Code以"08"开头)识别连接异常,并配合SetMaxOpenConns、SetConnMaxLifetime等配置及Ping()健康检查实现可靠连接管理。

GORM 本身不参与“平滑重启”,真正起作用的是底层 *sql.DB 连接池 + 应用层优雅关闭策略;重连不是靠手动触发,而是靠连接池在下次请求时自动新建连接。
为什么不能在重启时调用 gorm.Open 重建连接
很多人误以为服务重启就得重新 gorm.Open 一次,这是高危操作:
-
gorm.Open每次都会新建一个*sql.DB实例,旧池若未.Close()就会泄漏连接,导致数据库连接数暴涨 - 新池默认
SetMaxOpenConns(0)(无上限),在并发压测或流量突增时极易打满 MySQL 的max_connections - GORM v2 的回调、日志配置、
PrepareStmt等状态不会自动继承,重复初始化可能覆盖已有行为
真正的平滑发布依赖三个动作:优雅关闭、连接池参数预设、健康检查前置
关键不是“怎么重连”,而是“让旧连接自然退场、新连接按需上岗”:
- 启动时必须调用
db.DB().Ping()验证连通性,否则gorm.Open成功不代表 DB 可用 - 连接池参数要在初始化后立即设置:
SetMaxOpenConns(50)、SetMaxIdleConns(20)、SetConnMaxLifetime(7 * time.Hour)(略小于 MySQL 的wait_timeout) - HTTP 服务关闭前,调用
db.DB().Close()—— 这会阻塞直到所有空闲连接归还并关闭,但**正在执行的查询不受影响** - 反向代理(如 Nginx)需配置
proxy_next_upstream error timeout http_502,把失败请求转给新实例,而不是等旧实例硬扛
如何识别连接已断、避免请求卡死在僵尸连接上
不能依赖 db.Error != nil,它只是 GORM 操作级错误包装,和连接状态无关。必须穿透到驱动原生错误:
- PostgreSQL 场景:对
err做*pq.Error断言,检查Code是否以"08"开头(如"08006"表示 connection failure) - MySQL 场景:断言为
*mysql.MySQLError,关注Number()是否为2013(lost connection)、2002(can't connect) - 通用兜底:捕获
os.IsTimeout(err)或strings.Contains(err.Error(), "i/o timeout"),这类错误大概率是连接不可达或被中间件中断
最易被忽略的一点:连接池参数必须在 gorm.Open 后**立刻设置**,而不是等到第一次查询失败再补救——因为首次查询前,连接池里一个连接都没有,Ping() 才是唯一验证手段;而一旦上线,没人能保证下一次 First() 调用发生在哪个 goroutine 里。











