1. 不支持的查询类型
-
❌ 跨分片JOIN:非绑定表/广播表的JOIN
-
❌ 复杂子查询:分片键不一致的子查询
-
❌ 跨分片分页:
LIMIT 100000,10
类大偏移量查询 -
❌ 精确DISTINCT:跨分片去重结果可能不准确
-
❌ 存储过程/函数:全不支持
-
❌ UNION操作:跨分片UNION查询
规避方案:
用绑定表处理关联查询
将小表设为广播表
分页改用连续查询(
WHERE id > ? LIMIT n
)
2. 扩展机制与难点
扩展方式:
// 1. 自定义分片算法
class CustomSharding implements PreciseShardingAlgorithm<Long> {
String doSharding(Collection<String> targets, PreciseShardingValue<Long> value) {
return "ds_" + (value % 2); // 示例:按奇偶分库
}
}
// 2. 自定义分布式ID生成器
class SnowflakeGenerator implements KeyGenerator {
Comparable<?> generateKey() {
return Snowflake.nextId(); // 雪花算法
}
}
扩展难点:
-
🔥 SQL方言兼容性(如Oracle vs MySQL)
-
🔥 跨分片事务一致性保障
-
🔥 流式归并的内存控制
-
🔥 全路由查询的性能劣化
3. 关键场景解决方案
场景 | 解决方案 |
---|---|
跨分表范围查询 | 绑定表策略 + 范围分片算法(RangeShardingAlgorithm ) |
跨天数据查询 | 按时间分片(如log_202301 )+ create_time BETWEEN ? AND ? |
不停服扩容 | 双写 → 增量同步(Canal)→ 数据校验 → 灰度切流 → 下线旧分片 |
兜底方案 | 配置回滚 + 数据补偿(消息队列) + 熔断机制(maxConsecutiveErrors: 5 ) |
多库表数据聚合 | 分片本地聚合 → 内存归并(SUM/COUNT )→ 流式归并(大数据量) |
4. 核心流程剖析
执行特点:
-
解析引擎:基于ANTLR生成语法树
-
改写引擎:逻辑表名→物理表名(
t_order
→t_order_01
) -
归并引擎:
-
排序归并(
ORDER BY
) -
分组归并(
GROUP BY
) -
聚合归并(
SUM/COUNT
)
-
5. Sharding-JDBC vs MyCAT
维度 | Sharding-JDBC | MyCAT |
---|---|---|
架构 | 无中心化(客户端嵌入) | 中心化(独立代理层) |
性能 | ⭐⭐⭐⭐ 无网络跳转 | ⭐⭐ 有代理层开销 |
扩展性 | ⭐⭐⭐ 灵活定制分片算法 | ⭐⭐ 依赖插件机制 |
运维成本 | ⭐ 应用集成,无需独立部署 | ⭐⭐⭐ 需维护中间件集群 |
适用场景 | 高性能Java应用 | 多语言/遗留系统改造 |
选型依据:
优先Sharding-JDBC:云原生/高性能场景
选MyCAT:非Java语言/需MySQL协议代理
6. 分库分表规则设计
分片键选择:
-
高基数原则:用户ID > 订单ID > 时间戳
-
业务导向:高频查询条件字段(如
user_id
) -
避坑指南:
-
禁止选频繁更新的列
-
避免数据倾斜(如按性别分片)
-
经典分片策略:
sharding:
tables:
t_order: # 逻辑表名(应用程序中使用的表名)
actualDataNodes: ds${0..1}.t_order${0..15} # 物理表分布配置
databaseStrategy: # 分库策略配置
standard:
shardingColumn: user_id # 分库键(根据此字段值决定数据存到哪个库)
algorithmClassName: HashModDBSharding # 分库算法实现类
tableStrategy: # 分表策略配置
standard:
shardingColumn: order_id # 分表键(根据此字段值决定数据存到哪个表)
algorithmClassName: HashModTBSharding # 分表算法实现类
6.1 配置详解:
-
物理表分布 (actualDataNodes)
-
ds${0..1}
:2个数据库(ds0, ds1) -
t_order${0..15}
:每个库有16张物理表(t_order0 到 t_order15) -
总量:2(库) × 16(表) = 32个物理存储单元
-
-
分库策略 (databaseStrategy)
-
分片键:
user_id
-
算法:
HashModDBSharding
(自定义的取模算法) -
分库逻辑:
// 伪代码实现 public class HashModDBSharding { public String doSharding(String user_id) { int dbIndex = hash(user_id) % 2; // 对2取模 return "ds" + dbIndex; // 返回 ds0 或 ds1 } }
-
-
分表策略 (tableStrategy)
-
分片键:
order_id
-
算法:
HashModTBSharding
(自定义的取模算法) -
分表逻辑:
// 伪代码实现 public class HashModTBSharding { public String doSharding(String order_id) { int tableIndex = hash(order_id) % 16; // 对16取模 return "t_order" + tableIndex; // 返回 t_order0 到 t_order15 } }
-
6.2 数据路由示例:
假设插入一条订单记录:
INSERT INTO t_order (order_id, user_id, ...)
VALUES ("202308151234", "user_789", ...)
路由过程:
-
分库路由:
-
计算:
hash("user_789") % 2 = 1
-
目标库:
ds1
-
-
分表路由:
-
计算:
hash("202308151234") % 16 = 7
-
目标表:
t_order7
-
-
最终物理位置:
ds1.t_order7
6.3 配置特点分析:
-
双维度分片:
-
水平分库:按用户分散到不同数据库
-
水平分表:按订单分散到不同表
-
-
路由优势:
-
相同用户的订单 → 同一数据库(便于用户维度查询)
-
单个订单表数据量可控(分表解决单表过大问题)
-
-
扩容考虑:
-
库扩容:需修改取模基数(如 %2 → %3)
-
表扩容:需修改取模基数(如 %16 → %32)
-
-
潜在热点问题:
-
高频用户可能造成库热点(需配合用户分桶)
-
订单ID需保证哈希均匀性(避免表倾斜)
-
实际开发建议:
在算法类中添加日志,验证哈希分布均匀性
对高频用户实现分桶策略(如
user_id_bucket = user_id % 10
)使用一致性哈希减少扩容时的数据迁移量
7. 高频面试题速答
Q:如何解决跨分片分页?
A:
① 业务侧改用连续查询(WHERE id > ? LIMIT n
)
② 限制最大归并行数(max.connections.size.per.query: 5
)
③ 禁止大偏移量查询(业务层面拦截)
Q:怎么保证扩容时数据一致?
A:
① 双写阶段用事务消息确保新旧库原子写入
② 增量同步阶段通过binlog位点校对
③ 切换前用CRC32校验全量数据
Q:分布式ID如何生成?
A:
// 集成Snowflake
shardingRule:
defaultKeyGenerator:
type: SNOWFLAKE
props:
worker.id: ${server.port} % 1024