go项目中框架不支持分库分表,必须显式控制连接选择与sql路由;可用shardingsphere-proxy或自研路由,但需严格遵守逻辑库名、分片键显式传递、禁止物理表名、独立db实例、分表名显式拼接等约束。

Go 项目里所谓“框架支持分库分表”基本是误解——gorm、sqlx、gdb 都不提供分片能力,它们只面向单个 *sql.DB 实例;分库分表必须由你显式控制连接选择与 SQL 路由,要么靠中间件(如 ShardingSphere-Proxy),要么自己写路由逻辑。
ShardingSphere-Proxy 连接时必须用逻辑库名和确定分片键
Go 应用不能 import 任何 “ShardingSphere SDK”,只能把它当 MySQL 代理连:localhost:3307(默认 Proxy 端口),不是真实数据库的 :3306。关键约束有:
- 连接字符串中的
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 驱动(如go-sql-driver/mysql)不支持 XA 协议,实际不可用
自研分库路由必须预建独立 *sql.DB 实例并隔离连接池
别指望 database/sql 能动态切换 DB;每个物理库必须初始化独立的 *sql.DB,否则连接竞争、超时策略、事务边界都会出问题。实操要点:
- 用 map 预存所有库连接:
dbPool := map[int]*sql.DB{0: sql.Open(...), 1: sql.Open(...)} - 分库函数必须幂等无状态,推荐
userID % N或一致性哈希(如github.com/cespare/xxhash+ 环结构),避免用时间或 UUID 做分片键 - 每个
*sql.DB必须单独调SetMaxOpenConns、SetMaxIdleConns,不能共用配置 - 事务只能在单个
*sql.DB内生效;跨库写入需补偿机制(如本地消息表 + 重试)
分表逻辑必须在 DAO 层显式拼表名,ORM 的 Model 不起作用
gorm.Model(&Order{}) 永远绑定 struct 标签里的 table: "orders",不会自动变成 orders_202406。所有 CRUD 都得先算表名再操作:
- 封装纯函数生成表名:
func GetOrderTableName(created time.Time) string { return "orders_" + created.Format("200601") } - 用
db.Table(tableName).Create(&order),不是db.Create(&order) - 查询时 WHERE 条件必须含分表字段(如
created_at BETWEEN ? AND ?),否则无法确定查哪张表 - GORM v2 的
Session不继承Table()设置,每次操作都得显式调用
IN 查询和跨分片聚合必须在 Go 层合并结果
像 SELECT * FROM orders WHERE user_id IN (1001, 2005, 3017) 这种语句,三个 ID 可能落在不同库不同表。ShardingSphere-Proxy 能自动拆解,但自研路由必须手动处理:
- 对每个 ID 调用路由函数,得到一组
(*sql.DB, tableName)组合 - 并发执行 N 次查询(注意控制 goroutine 数量,防连接数爆炸)
- 内存中合并结果;若需
ORDER BY ... LIMIT,必须收拢全部数据后排序分页,无法下推到数据库 - 别依赖
UNION ALL——gorm不支持跨表 UNION,db.Raw()手写又难维护、易 SQL 注入
最容易被忽略的是:分片键必须参与每一次查询的 WHERE 条件,否则不是性能问题,而是功能错误——查不到数据、写错表、事务失效,全由此引发。别让“自动分片”的幻觉掩盖了这个硬约束。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











