Revel框架分库分表需预建连接池+请求上下文路由+模型层显式传参三者配合:在app/init.go的init()中手动创建多个独立gorm.DB实例并配置连接池参数,通过自定义filter提取分片键并注入对应DB至c.Params,模型方法必须显式接收gorm.DB参数,事务由controller显式控制。

Revel 框架不提供分库分表的自动路由或 SQL 重写能力,必须靠预建连接池 + 请求上下文路由 + 模型层显式传参三者配合实现;不存在“配置一下就自动分”的路径,所有分片逻辑都得你写死在 filter 和 service 里。
怎么初始化多个数据库连接池
不能依赖 Revel 的 database.conf 单连接机制,得在 app/init.go 的 init() 函数里手动调用 gorm.Open() 创建多个独立实例:
- 每个分片库(如
DBShard0、DBShard1)必须是单独的*gorm.DB变量,不能共用一个变量反复赋值 - 务必显式调用
db.DB().SetMaxOpenConns(20)和db.DB().SetMaxIdleConns(5),否则默认0会触发系统级连接限制 - DSN 中必须带上
parseTime=True&loc=Local,否则time.Time字段解析失败,报invalid time format - 不同分片若用 MySQL,建议在 DSN 末尾加
timeout=3s&readTimeout=3s&writeTimeout=3s,防止单个慢库拖垮整个请求
如何在请求中动态选择分片库
Revel 没有 AOP 或 SQL 解析器,无法根据 SQL 类型或参数自动分库。你得在自定义 filter 中完成识别和绑定:
- 用
revel.InterceptFunc(shardFilter, revel.BEFORE)注册 filter,确保它在 controller 执行前运行 - 从
c.Request.URL.Query().Get("shard_key")或c.Params.Get("id")提取分片键(如用户 ID),再通过哈希或查映射表决定走哪个库 - 用
c.Params.Set("db", DBShard0)把选中的*gorm.DB实例注入上下文,而不是存进c.Session(Session 不跨 goroutine) - 禁止在 filter 里做数据库查询——租户/分片识别阶段只做字符串运算或内存 map 查找
模型层怎么安全使用分片后的 DB 实例
模型方法不能引用全局 DBShard0 变量,否则并发下会复用已关闭或已 commit 的连接:
- 所有数据访问函数(如
FindUserByID、CreateOrder)必须把*gorm.DB作为第一个参数显式传入 - controller 中通过
c.Params.Get("db").(*gorm.DB)取出实例,再传给 service 和 repository 层 - 事务必须由 controller 显式开启:
tx := db.Begin(),然后把tx当作*gorm.DB一路透传,不能中途换回原始db - 慎用
db.Session(&gorm.Session{NewDB: true})——它只复制当前 scope,不切换底层连接池,对分库无效
最容易被忽略的是分片键的生命周期:一旦在 filter 中提取并计算出目标库,就必须确保后续所有 DB 调用都基于这个结果;如果 service 层又去解析一次 URL 或 header,就可能选错库,且这种错误在压测时才暴露。


















