GORM连接不能放在init()里,因为init()是同步单线程执行阶段,调用gorm.Open()或db.Ping()会阻塞整个程序启动,无法超时中断,导致K8s探针失败、首请求延迟等;应改用sync.Once配合context.WithTimeout在main中按需初始化并显式Ping。

为什么 GORM 连接不能放在 init() 里
Go 的 init() 函数是启动时强制同步执行的,一旦里面调用了 gorm.Open() 或 db.Ping(),就会卡住整个进程——哪怕数据库暂时不可达,或者网络延迟高,init() 就一直阻塞,应用根本起不来。K8s readiness probe 超时、本地调试启动慢、CI 构建后首请求延迟高,八成是这个原因。
sync.Once 是懒加载最安全的载体
把 GORM *gorm.DB 实例声明为包级变量,但初始化逻辑包裹在 sync.Once 的 Do() 中,确保只执行一次且线程安全。注意:Do() 内部不能含无 timeout 的阻塞调用,比如裸调 db.Ping();必须用 context.WithTimeout 包裹。
常见错误写法:
var dbOnce sync.Once
var DB *gorm.DB
<p>func GetDB() *gorm.DB {
dbOnce.Do(func() {
dsn := "user:pass@tcp(127.0.0.1:3306)/test"
DB, _ = gorm.Open(mysql.Open(dsn), &gorm.Config{}) // ❌ 没检查 error,也没 Ping
DB.Exec("SELECT 1") // ❌ 无 context 控制,可能永久卡住
})
return DB
}正确做法要点:
- 在
Do()内部显式调用db.PingContext(ctx),并设 3–5 秒超时 - 必须返回
error,不要log.Fatal()或panic,让上层决定重试或降级 - 避免在
Do()里做任何非必要 I/O:比如读配置文件、解析 YAML、调第三方 API
首次调用才连接,但得防并发抢跑
多个 goroutine 同时首次调用 GetDB() 时,sync.Once 会保证只有一个进入初始化,其余等待完成。但如果你在 Do() 里用了带超时的 PingContext,而数据库恰好临时抖动,第一次失败后,后续所有调用都会拿到 nil 的 DB —— 因为 sync.Once 不重试。
所以实际工程中建议:
- 把
GetDB()改成GetDB() (*gorm.DB, error),暴露错误供上层处理 - 如果业务能容忍“连接失败即返回 503”,那就直接透传 error;如果必须可用,可加简单重试(最多 2 次,每次间隔 200ms)
- 别在
Do()里初始化 logger、Redis 客户端等其他资源——每个资源应有独立的sync.Once和错误处理路径
连接池参数要在 gorm.Open() 后立刻设置
GORM 自身不管理连接池,底层依赖 sql.DB。懒加载初始化后,必须紧接着调用 DB.DB() 获取原生 *sql.DB,再设 SetMaxOpenConns、SetMaxIdleConns 等。否则默认值(如 MaxOpenConns=0 表示无限制)可能引发 MySQL 侧连接数打满。
示例关键片段:
func initDB() (*gorm.DB, error) {
dsn := buildDSN()
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxOpenConns(20)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(1 * time.Hour)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := sqlDB.PingContext(ctx); err != nil {
return nil, fmt.Errorf("failed to ping DB: %w", err)
}
return db, nil
}真正复杂的地方不在语法,而在于:你是否清楚每个 sync.Once 初始化块里,哪些操作是可重入的、哪些失败后必须中断、哪些该由 caller 承担重试责任——这些边界不划清,懒加载就只是把问题从启动时延后到了运行时某次随机请求上。


















