gorm 是 fiber 项目连接 mysql 最稳、最主流的方式,因其结构体映射、自动迁移、预编译查询、事务钩子等能力与 fiber 轻量特性天然契合,且官方明确支持、社区生态成熟;应避免手动使用 database/sql 或套用其他语言 orm 思路。

直接用 GORM 配合 mysql 驱动,是 Fiber 项目连接 MySQL 最稳、最主流的方式。别绕弯子试原生 database/sql 手动管理连接,也别硬套 Flask 那套 Flask-SQLAlchemy 思路——GORM 就是 Go 生态里为 Fiber/Gin 这类轻量框架量身定制的 ORM。
为什么选 GORM 而不是 raw database/sql
手动维护 *sql.DB 连接池、写 Scan 解析、处理事务嵌套、拼 SQL 字符串……这些在 Fiber 中既容易出错又重复。GORM 提供了结构体映射、预编译查询、自动迁移、钩子(BeforeCreate)、软删除等能力,且与 Fiber 的 ctx 生命周期天然契合。更重要的是,gorm.io/gorm 官方明确支持 Fiber 场景,社区示例和 issue 响应都集中在这一组合上。
常见错误现象:
• 直接在 fiber.Handler 里用 db.QueryRow 却忘记 defer rows.Close(),导致连接泄漏
• 多个并发请求争抢同一个未配置连接池的 *sql.DB 实例,出现 connection refused 或超时
• 手写 SQL 时没做参数化,被注入攻击
- 使用场景:CRUD 密集型 API(如用户管理、订单查询)必须用 GORM;纯只读报表类接口可考虑
sqlx+ 连接池复用 - 性能影响:GORM 默认开启 prepare statement,比裸
Exec略慢 3%~5%,但换来的是安全性和开发效率,值得 - 兼容性:GORM v1.25+ 完全支持 MySQL 8.0+(含 caching_sha2_password 插件),无需降级认证方式
初始化 GORM 并注入 Fiber 应用上下文
不要把 *gorm.DB 声明为全局变量,也不要每次请求都 Open 新连接。正确做法是:初始化一次,通过 Fiber 的 Ctx.Locals 或自定义中间件注入,或更推荐——注册进 Fiberhouse(如果你用它)的全局容器。
实操建议:
- 用
gorm.Open(mysql.Open(dsn), &gorm.Config{...})初始化,dsn格式为user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local - 务必调用
db.SetMaxOpenConns(100)和db.SetMaxIdleConns(20),否则默认 0(无限制)会压垮 MySQL - 在 Fiber 启动时执行
db.AutoMigrate(&User{}),避免上线后手动建表;生产环境建议关掉,改用migrate工具 - 把
*gorm.DB挂到app.Use(func(c *fiber.Ctx) error { c.Locals("db", db); return c.Next() }),后续 handler 里用c.Locals("db").(*gorm.DB)取
在 Handler 中安全使用数据库实例
别在 Handler 里直接用 db.Create(...),尤其涉及事务时。Fiber 的 Ctx 不自带事务上下文,GORM 的 Transaction 必须显式传入 *gorm.DB 实例。
常见错误现象:
• 在一个 Handler 里混用多个 db.Transaction 嵌套,没注意 defer tx.RollbackUnlessCommitted(),导致部分提交
• 把 db.Where("id = ?", id).First(&u) 写成 db.First(&u, id) 却没检查 errors.Is(err, gorm.ErrRecordNotFound),结果 panic
- 查询单条记录必须用
if err := db.Where(...).First(&u).Error; err != nil { ... },不能省略Error判断 - 更新操作优先用
db.Model(&u).Updates(map[string]interface{}{"status": "done"}),避免全量覆盖字段 - 批量插入超过 100 条时,改用
db.CreateInBatches(),防止 MySQL packet size 超限 - 敏感操作(如删账号)务必加日志:用
log.Info().Str("user_id", u.ID).Msg("user deleted"),别只打fmt.Println
连接池与超时配置的坑
MySQL 默认 wait_timeout 是 28800 秒(8 小时),但 GORM 连接池里的空闲连接如果超过这个时间未被复用,再次取出时就会报 invalid connection。这不是代码 bug,是配置失配。
实操建议:
- 在 DSN 中加
timeout=30s&readTimeout=30s&writeTimeout=30s,强制网络层超时 - 调用
db.SetConnMaxLifetime(60 * time.Second),让连接池主动淘汰老化连接 - 用
db.Callback().Create().After("gorm:create").Register("check_conn", func(tx *gorm.DB) { ... })注册连接健康检查(可选) - 监控指标:定期查
SHOW STATUS LIKE 'Threads_connected',值持续 > 150 就要调低MaxOpenConns
最容易被忽略的是:GORM 的 logger 默认不打印慢查询。上线前一定要加 config.Logger = logger.Default.LogMode(logger.Info),否则线上慢 SQL 你根本看不到。











