Revel框架不支持自动多数据库切换,需手动初始化DBMaster、DBSlave等独立实例并配置DSN参数;请求级DB注入须通过revel.InterceptFunc在BEFORE阶段完成,事务及写后读必须强制使用DBMaster。

Revel 框架不支持自动多数据库切换,所有库的初始化、路由和注入都得手动控制——你写错一个 init() 位置或漏掉一次 c.Params.Set(),请求就可能连到错误的库甚至 panic。
多数据库必须用独立全局变量预初始化
Revel 不会帮你管理多个 *gorm.DB 实例的生命周期,也不能靠“配置开关”动态加载。你得在 app/init.go 的 init() 或 AppInit() 中显式创建每个库的连接:
- 每个库用不同变量名,比如
DBMaster、DBSlave、DBAnalytics,不能复用同一个变量再赋值 - 每个
gorm.Open()必须配独立 DSN,并调用db.DB().SetMaxOpenConns(20)等底层池参数(gorm.Config不管这个) - MySQL DSN 务必带
parseTime=true&loc=Local,否则time.Time字段解析失败是静默的(查不到值,也不报错) - 别在 handler 里重复
gorm.Open()——连接池不会复用,socket 和 goroutine 会指数级增长
请求级 DB 注入不能靠全局变量裸用
把 DBMaster 直接 import 到 controller 或 model 里调用,是并发 panic 的高发场景:事务状态残留、Session 配置污染、Preload 错乱都会发生。
- 必须用
revel.InterceptFunc在revel.BEFORE阶段注入,例如:c.Params.Set("db", DBMaster) - 若需主从分离,filter 内要根据请求路径或 header 判断,比如
if c.Request.URL.Path == "/api/stats" { c.Params.Set("db", DBAnalytics) } - controller 中取值必须类型断言:
db := c.Params.Get("db").(*gorm.DB),不能跳过这步直接用 - 模型方法(如
User.FindByID)应接收*gorm.DB为参数,而不是引用包级变量
事务与写后读必须强制走 master
Revel 没有 SQL 解析器,也不会拦截 SELECT 自动路由到 slave。所有一致性保障全靠你手动兜底。
- 事务内所有操作(哪怕含
SELECT)必须用DBMaster,跨库事务会直接失败 - 写操作后立刻读(如注册完跳转个人页),不能调用通用查询函数,必须显式传
DBMaster - 从库 DSN 建议加
read_only=1(如mysql://user:pass@rds-readonly:3306/db?read_only=1),防误写 - 主从延迟敏感场景,可加本地缓存(如
sync.Map)暂存刚写入的 ID → 结果,有效期设为 1–2 秒
最易被忽略的是环境配置隔离:多数据库的 DSN 必须放在 app.conf 对应的 [dev]、[prod] 块里,而不是全局区;同时环境变量引用(如 ${DB_MASTER_URL})必须提前 export,Revel 启动时只展开一次,运行时不会刷新。

















