GORM 不支持跨库分片,仅管理单个 *gorm.DB 实例的连接池;跨库需手动初始化多个独立实例并按分片键路由。

GORM 本身不管理分库分表连接池,你得自己建、自己选、自己传——它只管单个 *gorm.DB 实例内的连接池。
为什么不能用 db.Table("xxx") 实现跨库分片
这个调用只是动态切换当前 *gorm.DB 实例操作的表名,底层仍走同一个数据库连接池。哪怕你写 db.Table("users_001"),只要没换 *gorm.DB 实例,就永远在同一个物理库(比如 shard_a)里查表,根本到不了 shard_b。
- 常见错误现象:
SELECT * FROM users_001执行成功,但数据全在shard_a,shard_b空着没人写 - 本质是混淆了「逻辑表名」和「物理库路由」:GORM 没有内置库级路由能力
- 如果你看到某段代码里
db.Sharding(...)或db.UseShard(...),那一定是封装层或第三方扩展,不是 GORM 官方 API
手动管理多个 *gorm.DB 实例的实操要点
每个分片库必须独立调用 gorm.Open(),不能复用同一个 *sql.DB 或共享配置。
- 启动时预初始化:在
app.Init()或服务启动入口处,为每个分片创建带唯一标识的*gorm.DB,例如dbShard0、dbShard1 - 连接池参数要差异化:主库可设
MaxOpen=200,从库/分片库按读写比配MaxOpen=80~120,避免争抢连接 - 别共用
gorm.Config实例:不同库可能需要不同Logger、NowFunc或DisableForeignKeyConstraintWhenMigrating设置 - 迁移必须逐个执行:
dbShard0.AutoMigrate(&User{})和dbShard1.AutoMigrate(&User{})得分开调,不能只跑一次
ShardingSphere-Proxy 整合时的连接配置陷阱
Go 应用连的是 Proxy,不是真实 MySQL;所有分片逻辑由 Proxy 解析 SQL 后路由,GORM 层完全无感。
- DSN 中的
database填逻辑库名(如shop),不是物理库名(如shop_0) - SQL 必须显式带分片键值:
WHERE user_id = 12345可行,WHERE user_id = ?在某些 Proxy 版本下会路由失败 - 禁止硬编码物理表名:
SELECT * FROM orders_202405会绕过 Proxy,报错Table 'db.orders_202405' doesn't exist - 事务默认是单库本地事务;开启 XA 需 Proxy 配置
transaction_type: XA,但多数 Go MySQL 驱动不支持该协议,实际不可用
Revel/Gin 场景下如何安全传递分片 DB 实例
不能改全局 *gorm.DB,也不能在 handler 里临时 gorm.Open()——连接池生命周期和上下文绑定是关键。
- 请求进来时,根据分片键(如
user_id % 4)算出目标分片,从预初始化池中取出对应*gorm.DB - 通过
c.Context(Gin)或r.Request.Context()(Revel)注入,模型层统一用c.MustGet("db").(*gorm.DB)拿实例 - 避免在中间件里直接调
db.Create():模型方法应接收*gorm.DB作为参数,而不是依赖闭包或全局变量 - 注意 context 超时:DB 实例本身不带超时,但查询需配合
WithContext(ctx),否则可能卡住整个请求
最易被忽略的一点:跨分片聚合操作(比如 JOIN 多个分片的订单和用户)必须在 Go 层手动合并结果,Proxy 和 GORM 都不会帮你做这件事——它不在 SQL 层面支持,也不在 ORM 层抽象。你得自己发多次查询、去重、排序、分页。











