纯gorm无法实现跨库join或聚合查询,必须由应用层拆解分发合并;preload和joins均失效,需手动分库查询+内存归并;分页须用游标,count需各库求和,事务隔离需补偿机制。

纯 GORM 无法实现真正的跨库 JOIN 或聚合查询,所谓“分布式查询”必须由应用层拆解、分发、合并——没有中间件或代理时,只能接受性能/一致性/开发成本的三选二。
Preload 在跨库场景下完全失效
Preload("Orders") 依赖单个 *gorm.DB 实例执行子查询,而跨库意味着 Orders 可能在 order_db_0,User 在 user_db_1。GORM 不会、也不能自动跨连接池发起关联查询。
- 调用
db.Preload("Orders").First(&user)后,user.Orders一定是空切片,且日志里只有一条 SELECT users.* 的 SQL - 即使你手动在两个库上分别查出数据,GORM 也不会帮你做内存级关联匹配——它不是 ORM 的职责
- 试图用
Joins("orders")更危险:SQL 会直接报错Unknown database 'orders'或Table 'order_db_1.orders' doesn't exist
手动分发 + 内存合并是唯一可行路径
你要自己决定哪部分逻辑放数据库、哪部分放 Go 层。典型做法是:主表查 ID 列表 → 并行查各分库子表 → Go 层按外键字段(如 user_id)归并。
- 先查
SELECT id FROM users WHERE status = ?得到[]uint64 - 按
user_id % 4把 ID 分组,投递给对应dbShards[i]执行db.Table("orders_0").Where("user_id IN ?", ids).Find(&orders) - 用
map[uint64][]Order做索引,再遍历 users 构建最终响应结构体 - 注意:不能直接用
db.Raw(...).Scan(&orders)扫描进带嵌套结构的 struct,GORM 不支持跨模型字段映射;得用扁平 DTO 或分步 Scan
分页与 COUNT(*) 必须降级处理
跨库场景下,SELECT COUNT(*) FROM users JOIN orders 没有等价实现。你得接受近似或延迟统计。
-
COUNT(*)只能各库分别执行再求和,但若涉及 WHERE 条件(如orders.status = 'paid'),就必须先取 ID,再分库查子表,最后累加——性能随分片数线性下降 - OFFSET/LIMIT 分页不可靠:各库返回的第 20–30 条记录无法保证全局顺序,必须改用游标(如
WHERE id > ? ORDER BY id LIMIT 10)并逐库合并后重排序 - 如果业务允许,把高频聚合结果冗余到单独汇总表(如
user_stats),定时任务更新,避免每次请求都跨库扫描
最易被忽略的一点:所有跨库操作都默认放弃事务隔离。哪怕你在每个分库上都用了 db.Transaction(),它们之间仍是独立事务——一个成功一个失败时,没有回滚机制。补偿逻辑(如发 MQ、写对账表)不是可选项,而是必选项。











