分库分表需手动实现路由、聚合与事务补偿,orm如gorm不支持自动分片;应使用map缓存分片db连接,幂等计算shardid,避免跨分片join,count和分页需各分片执行后合并,跨分片事务改用最终一致性。

分库分表不是 ORM 能自动解决的事
Go 生态里没有像 MyBatis-ShardingSphere 那样开箱即用的分库分表中间件,gorm 或 sqlx 本身不处理路由逻辑。你得自己决定:数据按什么字段切(比如 user_id)、切到哪个库哪个表、事务怎么保、跨分片查询怎么写。框架只提供连接和执行能力,路由、聚合、重试都得手写。
用 shard + dbMap 管理分片映射关系
别硬编码 db0/db1 字符串,也别在每次查询前拼接 DSN。推荐用 map 做运行时路由缓存:
var dbMap = map[string]*sql.DB{
"shard_0": db0,
"shard_1": db1,
"shard_2": db2,
}
func getDBByUserID(userID int64) *sql.DB {
shardID := userID % 3 // 简单取模,实际可用一致性哈希
return dbMap[fmt.Sprintf("shard_%d", shardID)]
}
-
shardID计算必须幂等且可预测,不能依赖随机或当前时间 - DB 连接要提前初始化并验证,避免首次请求时 panic
- 如果分片数可能动态增减,
dbMap得支持热更新,但注意并发读写安全
跨分片 JOIN 和 COUNT(*) 必须手动拆解
MySQL 不知道你的分片逻辑,SELECT * FROM user JOIN order 这种语句会发到单个库,结果错漏。真实场景下:
- 关联查:先查
user得到一批user_id,再按user_id分组路由到对应order分片并发查询,最后 Go 层合并 - 聚合查:
COUNT(*)要发到所有分片,sum各自结果;ORDER BY created_at LIMIT 10得各分片取 top 20,再全局排序取前 10 - 事务:跨分片 update 几乎必然放弃强一致性,改用本地事务 + 最终一致补偿(比如发 MQ)
go-sql-driver/mysql 连接池配置直接影响分片吞吐
每个分片 DB 实例都配独立连接池,SetMaxOpenConns 和 SetMaxIdleConns 不能照搬单库配置:
- 总连接数 = 分片数 × 单库最大连接数,别让 MySQL 的
max_connections被打爆 - 高并发下,
SetConnMaxLifetime设太短会导致频繁重建连接,建议 1h~4h - 开启
parseTime=true和loc=UTC避免时区错乱,尤其涉及分片键是时间字段时
分片键设计、路由兜底策略、失败重试粒度——这些细节没写进代码,上线后就会变成半夜告警的根源。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











