热重载时GORM连接池重复初始化会导致panic,因main()反复执行且未关闭旧池,引发数据库连接数超限;须将gorm.Open()移至main()中单次调用、显式设连接池参数、Ping校验后执行AutoMigrate(仅开发环境),并用defer db.Close()确保释放。

热重载时 GORM 连接池重复初始化会导致 panic
用 air 或类似工具做热重载时,main() 会反复执行,如果 GORM 的 gorm.Open() 调用写在包级变量或 init() 函数里,每次重载都会新建一个连接池——旧连接池没被显式关闭,新池又占资源,很快触发 PostgreSQL 的 too many clients already 或 MySQL 的 Too many connections 错误。
- 不要在
model/目录下任何init()里调用gorm.Open() - 数据库实例(如
*gorm.DB)必须是函数返回值,不能是包级全局变量 - 确保
database.Init()这类初始化函数只在main()中调用一次,且不被热重载逻辑意外多次触发
AutoMigrate 必须在连接池就绪后执行,且仅限开发环境
AutoMigrate() 不是“热重载专属功能”,它本质是 DDL 操作,生产环境禁止自动执行。热重载期间若每次启动都跑一遍,可能因字段类型变更导致迁移失败(比如从 string 改为 json.RawMessage),或并发重载时多个进程同时执行 DDL 引发锁表。
- 用
viper.GetBool("app.debug")控制是否启用AutoMigrate,生产配置中设为false -
AutoMigrate()前必须确认*gorm.DB已成功 Ping 通,否则会静默失败 - 避免在
Register(&Model{})时直接触发迁移;应统一在database.Init()最后一步集中调用
连接池参数必须显式设置,不能依赖默认值
GORM 默认的 MaxOpenConns=0(不限制)和 MaxIdleConns=2 在热重载场景下极危险:每次重载新建连接池,空闲连接不会跨周期复用,连接数指数增长。
- 硬编码设置
db.Config.ConnPool.MaxOpenConns = 20和MaxIdleConns = 10(根据你的 DB 实例规格调整) - 加上
ConnMaxLifetime和ConnMaxIdleTime,例如30m和5m,防止连接僵死 - 务必在
gorm.Open()后立即调用db.SetMaxOpenConns()等方法,而不是等中间件或 handler 里再设
热重载期间无法安全复用旧连接池
别试图在重载时“保留”旧 *gorm.DB 实例——Go 进程重启后,底层 TCP 连接、内存地址、goroutine 上下文全部失效,强行复用只会触发 invalid connection 或 context canceled。
- 接受“每次重载就是全新进程”的事实,把初始化逻辑收敛到单一入口(如
cmd/main.go的main()) - 所有模型注册(
Register(&User{}, &Post{}))只负责收集类型,不触发任何 DB 操作 - 用
defer db.Close()确保每次进程退出前释放连接,哪怕只是热重载退出
真正容易被忽略的是:GORM 的 AutoMigrate() 不校验字段变更的兼容性,比如把 gorm.ColumnType{Type: "varchar(255)"} 改成 "text",PostgreSQL 可能允许,但 SQLite 会报错且不提示具体原因——这种错误只在热重载第一次启动时暴露,之后进程已死,日志也随进程销毁。











