GORM本身不支持分库分表,需依赖中间件如go-gorm/sharding实现;其本质是通过拦截Statement动态替换表名和连接池,但仅适用于简单哈希/范围分片场景,复杂需求需TDDL或自研路由层。

分表分库不是GORM原生能力,得靠中间件或手动路由
GORM本身不提供分库分表逻辑,它只负责单库单表的CRUD映射。你看到的go-gorm/sharding这类项目,本质是给GORM加了一层“表名动态生成+DB连接选择”的拦截器,并非GORM内建功能。所以别指望调个db.Shard("user_001").Find(&u)就能自动分片——那只是封装后的语法糖,底层仍需你明确控制路由规则和连接池管理。
sharding中间件选型:轻量级场景优先考虑go-gorm/sharding
如果你的业务分片规则简单(比如按用户ID取模分16张表),且不涉及跨分片JOIN、全局唯一ID、分布式事务,go-gorm/sharding足够用。它直接挂钩GORM的Statement生命周期,在Process阶段替换TableName和ConnPool。
- 必须手动注册分片规则,例如:
sharding.Register("user", sharding.NewHashSharding(16, "id")) - 所有查询必须显式调用
db.Shard("user", userID).Where(...).Find(),否则走默认表 - 不支持
FindInBatches自动分批跨表扫描——它只对单张物理表生效,跨表需你自己循环分片键范围 - 连接池需独立配置,不能复用主
*gorm.DB的sql.DB,否则会混用连接
复杂场景绕不开TDDL或自研路由层
当出现以下任一情况,go-gorm/sharding就力不从心了:
- 需要根据时间字段分表(如
order_202601、order_202602),而go-gorm/sharding只支持哈希/范围路由,不解析SQL中的WHERE created_at > ? - 要执行
SELECT * FROM user WHERE tenant_id = ? AND status = ?,但tenant_id才是分片键,status只是普通条件——中间件必须能识别并提取分片键,否则可能路由到错误库 - 有跨分片聚合需求(如统计全量订单数),
go-gorm/sharding无法合并结果,得靠应用层fan-out + reduce - 要求读写分离+分片组合策略,比如写主库分片A,读从库分片B,这已超出单一分片插件职责
这时候该评估TDDL或基于gRPC+etcd自建路由中心——前者重但成熟,后者可控但开发成本高。
别忽略分片键设计与GORM配合的硬约束
无论用哪个方案,分片键的选择直接影响GORM使用方式:
- 分片键必须出现在
WHERE条件中,否则中间件无法路由,GORM会报table not found或查空数据 - 避免在
ORDER BY或GROUP BY中依赖非分片键字段,否则排序/分组结果不准(各分片独立执行) -
FindInBatches只能用于单分片内批量处理,若要扫全量数据,得先获取所有分片键值(如所有tenant_id列表),再逐个db.Shard("order", tid).FindInBatches(...) - 外键约束失效:分库后,
user和order可能在不同库,GORM的Preload会失败,必须改用应用层JOIN或冗余字段
最常被忽略的一点:分片后AutoIncrement主键不再全局唯一,GORM的Create返回的ID仅在本分片有效。如果业务依赖ID做幂等或关联,得换Snowflake或号段模式生成ID。


















