初始化GORM必须传dialector实例和*Config,如mysql.Open(dsn)与&gorm.Config{};DB不可裸声明,应封装为可注入对象并配置连接池。

初始化 GORM 时必须用对 Open 的参数类型
很多人卡在第一步:调用 gorm.Open 报错 cannot use "xxx" (type string) as type gorm.Dialector。这是因为 GORM v2 不再接受数据库连接字符串,而是要求传入具体驱动的 Dialector 实例。
比如用 MySQL,得先 go get gorm.io/driver/mysql,再这样写:
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
- SQLite 只需
go get gorm.io/driver/sqlite,然后用sqlite.Open("test.db") - PostgreSQL 对应
postgres.Open(...),驱动是gorm.io/driver/postgres - 别漏掉
&gorm.Config{}—— 空结构体也必须传,否则编译不通过
DB 实例不能全局裸变量,得封装成可注入对象
直接在包级声明 var DB *gorm.DB 看似简单,但会导致测试难、迁移难、多数据源冲突。Fiber 启动时应该把 *gorm.DB 注入到 fiber.App 或自定义上下文里。
推荐做法是在 internal/pkg/database 下建 InitDB() 函数,返回 *gorm.DB,并在 cmd/main.go 中完成初始化和绑定:
app := fiber.New()
db, _ := database.InitDB()
app.Use(func(c *fiber.Ctx) error {
c.Locals("db", db)
return c.Next()
})
- 后续 handler 里用
c.Locals("db").(*gorm.DB)拿实例,避免 import 循环 - 如果要用事务,别在中间件里开——事务生命周期必须由 handler 控制,否则 panic 或锁表
- 别忘了调用
db.Exec("PRAGMA foreign_keys = ON")(SQLite)或db.Exec("SET FOREIGN_KEY_CHECKS = 1")(MySQL)来启用外键约束
迁移(Migration)必须显式执行,且不能依赖 AutoMigrate 做生产变更
db.AutoMigrate(&Dog{}) 很方便,但它只做“加字段”“建表”,不会删字段、改类型、重命名,也不支持回滚。开发阶段可以,上线前必须换成熟方案。
正确路径是:
- 用
migrate工具(如github.com/golang-migrate/migrate)管理 SQL 脚本 - 脚本按
000001_init.up.sql/000001_init.down.sql命名 - 启动时检查环境变量
ENV=prod,prod 下禁止调用AutoMigrate,只运行 migrate up - SQLite 迁移要额外注意:它不支持
ALTER COLUMN,字段类型变更只能靠重建表 + 数据复制
连接池配置不设限,高并发下会耗尽数据库连接
默认 GORM 的连接池最大打开数是 0(无限制),而 MySQL 默认最大连接数通常只有 151。线上一压测就报 ERROR 1040: Too many connections。
必须显式配置:
sqlDB, _ := db.DB() sqlDB.SetMaxOpenConns(25) sqlDB.SetMaxIdleConns(25) sqlDB.SetConnMaxLifetime(time.Hour)
-
SetMaxOpenConns建议设为 DB 服务器 max_connections 的 1/3~1/2 -
SetMaxIdleConns应 ≤SetMaxOpenConns,否则空闲连接占着坑不释放 -
SetConnMaxLifetime必须设(尤其云数据库),避免连接因网络抖动或服务端超时被静默断开
if err != nil { log.Printf(...) } 吞掉——否则监控永远看不到慢查询或死锁。


















