Kratos本身不处理分库分表路由,仅提供数据访问封装;分片必须由外部组件(如ShardingSphere-Proxy或客户端SDK)实现,Kratos只需接入一个“逻辑单库”式的*sql.DB实例即可。

Kratos 本身不处理分库分表路由
Kratos 是一个微服务框架,不是数据库中间件。它不提供 sharding、route to shard 或 split table 能力。如果你在 Kratos 服务里直接写 db.Query("SELECT * FROM users_001 WHERE id = ?"),那只是硬编码分片逻辑,和 Kratos 无关,也完全不可维护。
常见错误现象:以为加个 ShardingDataSource 配置就能自动路由,结果发现 Kratos 的 sql.DB 根本不识别这个概念;或者试图在 data.UserRepo 里 if-else 切库,导致业务代码和分片策略强耦合、测试困难、扩容时改一堆地方。
- Kratos 的
data层只负责封装数据访问,它期望你传入一个可用的*sql.DB或 ORM 实例 - 真正的分库分表路由必须由外部组件完成:要么是客户端分片 SDK(如 ShardingSphere-JDBC 的 Go 兼容层),要么是服务端代理(如 ProxySQL、ShardingSphere-Proxy、MyCat)
- 如果用 Kratos 做 BFF 或聚合层,它最多能做“按业务参数选择下游分片服务”,比如
user_id % 4 == 0 → call user-service-shard-0,但这属于服务发现层面的路由,不是 SQL 级别分片
怎么让 Kratos 服务对接分片数据库
核心思路是:把分片逻辑下沉到数据源层,对 Kratos 的 data 层透明。Kratos 只需要拿到一个“看起来像单库”的 *sql.DB 实例即可。
- 推荐方案:用
ShardingSphere-Proxy作为 MySQL 兼容代理,Kratos 连接它就像连普通 MySQL 一样,SQL 里的WHERE user_id = ?会被 Proxy 自动路由到对应分片 —— 此时 Kratos 完全无感 - 若必须客户端分片(如性能压测要求绕过网络跳转),可用轻量库如
kratos-sharding(社区非官方)或自己封装shardDB类型,实现sql.DB接口,在Exec/Query时解析 SQL + 参数,计算分片键后分发到对应物理连接 - 注意连接池管理:每个物理库应有独立
*sql.DB实例,并设置合理SetMaxOpenConns,避免一个分片打满拖垮全局连接数 - 事务要特别小心:跨分片事务只能靠最终一致性(发 MQ)或 Saga 模式;ShardingSphere-Proxy 支持 XA,但性能折损明显,Kratos 服务内不要假设
tx.Commit()能跨库原子生效
分片键设计如何影响 Kratos 数据层代码
分片键不是随便选的字段。它直接决定你的 data.UserRepo 方法签名是否可扩展、是否能走索引、是否支持分页。
- 必须在
GetUser(ctx, id uint64)的id上做取模分片?那没问题,查询天然路由;但如果用GetUserByPhone(ctx, phone string),而phone不是分片键,就会触发广播查询(查所有分片),性能雪崩 - 避免在
data层写SELECT * FROM users WHERE status = 1 LIMIT 20—— 没带分片键的查询默认扫全库,Kratos 拦不住,DB 代理也拦不住 - 深分页(
OFFSET 10000)在分片环境下失效:各分片返回自己的 offset 结果再合并,语义已错。必须改用游标分页(WHERE id > ? ORDER BY id LIMIT 20),且确保游标字段是分片键或带唯一索引 - 如果业务强制要求多条件查询(如
user_id + created_at),建议冗余宽表,或用 Elasticsearch 同步数据,别指望分片 DB 原生支持
为什么别在 Kratos service 层做分片判断
有人想在 service.UserService 里根据请求参数决定调哪个 data.UserRepo 实例,这会破坏分层契约,带来三重问题。
- 单元测试爆炸:每个方法都要 mock 多个 repo 实例,还要验证路由逻辑是否正确
- 事务失控:service 层开启的
tx只作用于单个*sql.Tx,无法跨 repo 实例传播 - 可观测性断裂:Kratos 的 tracing 和 metrics 默认按 handler 统计,分片逻辑藏在 service 里,慢查询根本定位不到是哪个物理库拖慢的
- 真正该放分片逻辑的地方只有两个:数据库代理层(推荐),或 data 层最底层的 driver 封装(需自研,慎用)
最易被忽略的一点:分片键变更(比如从 user_id 改成 tenant_id)时,如果逻辑散落在 service 和 data 多处,上线窗口极短、回滚困难;而集中到数据源层,只需替换一个组件,业务代码零改动。



















