revel中连接mysql必须用gorm v2(gorm.io/gorm + gorm.io/driver/mysql),启动时在app/init.go初始化全局*gorm.db,配置parsetime=true、loc=local及连接池参数,并做连通性校验。

Revel 框架本身不内置数据库驱动,连接 MySQL 必须靠第三方 ORM 或原生 SQL 包——实践中 gorm.io/gorm 是最主流选择,且 GORM v2 对 MySQL(gorm.io/driver/mysql)支持稳定、连接池开箱即用。
在 app/init.go 中初始化 GORM MySQL 连接
别在 controller 里临时 gorm.Open(),那会导致每次请求都新建连接池,资源爆炸。必须在应用启动时一次性初始化,并作为全局变量或依赖注入使用。
-
DB变量应声明为*gorm.DB类型,不是gorm.DB(后者是结构体值,无法安全复用) - 连接字符串里必须显式指定
parseTime=true和loc=Local,否则time.Time字段会解析失败或时区错乱 - 务必调用
DB.Exec("SELECT 1")或DB.Migrator().CurrentDatabase()做启动时连通性校验,避免上线后才发现配置错误
示例片段:
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
var DB *gorm.DB
func init() {
dsn := "user:password@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
revel.AppLog.Fatal("failed to connect database: ", err)
}
}
为什么不能直接用 database/sql + mysql 驱动
能连上,但很快会出问题:Revel 的 revel.InterceptFunc 和 revel.BEFORE filter 机制依赖上下文传递,而原生 *sql.DB 没有内置事务传播、钩子、预编译语句复用等能力。你得自己封装 BeginTx、自己管理 defer tx.Commit()、自己处理 panic 回滚——GORM 已经把这些跑通了。
- 手动用
database/sql时,tx.QueryRow()返回的sql.NullString等类型和 Revel 模板渲染不友好,容易空指针 - GORM 的
First()、Where()、Preload()能自然融入 Revel 的 controller 流程;裸 sql 则要反复写rows.Scan(),模板层还得手动 map 字段 - Revel 日志中间件默认识别 GORM 的
logger接口,可自动打印慢查询;裸 sql 不会触发
连接参数里最容易漏掉的三个关键项
MySQL 默认配置对 Web 应用太“宽松”,不加限制会在高并发下迅速耗尽连接或卡死。
-
maxOpenConns=20:不设上限时,GORM 默认是 0(无限制),瞬间几百请求就打爆 MySQL 的max_connections -
maxIdleConns=10:空闲连接数太少会导致频繁建连,太多又浪费内存;设为maxOpenConns的 1/2 是较稳的选择 -
connMaxLifetime=1h:防止连接长期空闲被 MySQL 的wait_timeout主动断开,引发 “invalid connection” 错误
完整 dsn 示例(含连接池参数):
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local&timeout=5s" sqlDB, _ := DB.DB() sqlDB.SetMaxOpenConns(20) sqlDB.SetMaxIdleConns(10) sqlDB.SetConnMaxLifetime(time.Hour)
真正麻烦的从来不是“怎么连上”,而是“连上之后怎么让每个 HTTP 请求都拿到正确、隔离、可追踪的连接实例”。Revel 的 filter 机制和 GORM 的 Session() 方法配合使用,才是分库、读写分离、事务控制的起点——这点常被跳过,结果上线后查不出谁 hold 住了连接。











