GORM v2 连接 MySQL 必须用 mysql.Open(dsn) 包装 DSN,不可直传字符串;需手动配置底层 *sql.DB 连接池参数;AutoMigrate 仅建表、加字段、设非空,不删字段、不改类型、不加索引;事务须显式 Begin/Commit 并用 Session 隔离。

gorm.Open 传参必须用 mysql.Open(dsn),不能直接传字符串
常见错误是把 DSN 字符串直接塞进 gorm.Open 第一个参数,比如写成 gorm.Open(dsn, &gorm.Config{}) —— 这会 panic 报错 "failed to initialize database, invalid driver" 。GORM v2 要求显式指定驱动工厂函数,MySQL 必须用 mysql.Open(dsn) 包一层。
正确写法:
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
<p>dsn := "user:pass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
</p>
注意:mysql.Open 是函数调用,不是类型名;dsn 中的 parseTime=True 和 loc=Local 缺一不可,否则 time.Time 字段会解析失败或时区错乱。
连接池要手动配置 sql.DB,gorm.DB 本身不管理最大连接数
gorm.Open 返回的 *gorm.DB 是一个高层封装,底层的 *sql.DB 才真正控制连接池。如果你不显式提取并配置它,Go 默认的 MaxOpenConns=0(无限制)和 MaxIdleConns=2 极易在高并发下打爆 MySQL 连接数。
必须这样初始化后立即配置:
sqlDB, err := db.DB() // 获取底层 *sql.DB
if err != nil {
panic(err)
}
sqlDB.SetMaxOpenConns(20)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxIdleTime(30 * time.Minute)
几个关键点:
-
SetMaxOpenConns建议设为 MySQL 的max_connections的 70% 左右,避免争抢 -
SetConnMaxLifetime推荐 1 小时,防止长连接因网络抖动僵死 -
SetConnMaxIdleTime设为 30 分钟,比 MySQL 的wait_timeout略短即可
AutoMigrate 不会自动加索引或改字段类型,仅建表/增列
很多人以为 db.AutoMigrate(&User{}) 能“全量同步”结构,实际上它只做三件事:建不存在的表、加缺失的字段、给字段加非空约束(如果 struct tag 有 not null)。它不会:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 删除废弃字段(旧字段还在表里)
- 修改已有字段类型(比如从
string改成text) - 添加索引(哪怕你写了
indextag) - 调整默认值(
defaulttag 只在首次建表生效)
所以生产环境必须配合 SQL 手动维护:
// 添加联合索引
db.Exec("CREATE INDEX idx_user_email_status ON users(email, status)")
<p>// 修改字段类型(MySQL)
db.Exec("ALTER TABLE users MODIFY COLUMN bio TEXT")
</p>
开发阶段可加 logger 查看 GORM 实际执行的 SQL,避免误判迁移效果。
事务嵌套需用 Session + Context,不能靠 defer db.Transaction
写事务时容易想当然用 defer 包一层 db.Transaction,但 GORM 的 Transaction 方法返回的是新 *gorm.DB 实例,不是原实例的“开关”。直接 defer 会导致事务未提交就释放,或 panic 报 "invalid transaction"。
正确方式是显式控制生命周期,并用 Session 隔离上下文:
tx := db.Session(&gorm.Session{NewDB: true}).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Create(&order).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&payment).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
关键细节:
-
Session(&gorm.Session{NewDB: true})确保新开独立会话,避免污染主 DB 实例 - 不要依赖
defer tx.Rollback(),因为 panic 后 rollback 可能失效 - 所有操作必须用
tx实例,不能混用db
最常被忽略的是:GORM 的事务不跨 goroutine,一旦启了新协程,就得重新 Begin,且无法共享同一事务上下文。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










