主题
十三、ShardingSphere(4 题)
13.1 ★★★★ShardingSphere 的核心组件有哪些?ShardingSphere-JDBC、ShardingSphere-Proxy、ShardingSphere-Sidecar 的区别和适用场景是什么?分片原理是什么?
📖 点击查看答案
核心组件:
- Sharding-JDBC:轻量级 Java 框架,增强版 JDBC 驱动,直连数据库
- Sharding-Proxy:独立部署的数据库代理,屏蔽分库分表
- Sharding-Sidecar:基于 Service Mesh 的sidecar 方案
- 核心功能模块:分库分表、读写分离、数据加密、影子库、分布式事务、弹性伸缩
三种形态对比:
| 维度 | Sharding-JDBC | Sharding-Proxy | Sharding-Sidecar |
|---|---|---|---|
| 部署方式 | 嵌入应用 | 独立部署 | 与应用同 Pod |
| 语言支持 | 仅 Java | 多语言 | 多语言 |
| 性能损耗 | 低(直连) | 较高(多一跳) | 低 |
| 运维复杂度 | 低 | 高 | 中 |
| 适用场景 | Java 单体/微服务 | 异构语言、DBA 运维 | K8s 云原生 |
分片原理:
- SQL 解析:词法解析 → 语法解析 → 生成抽象语法树(AST)
- SQL 路由:根据分片键和分片算法,定位目标库表(分片路由、广播路由、单播路由)
- SQL 改写:将逻辑 SQL 改写为真实库表 SQL(如
t_order→t_order_0) - SQL 执行:并行执行多库 SQL,通过 ExecutorEngine 线程池
- 结果归并:合并多库结果集(流式归并、内存归并、装饰者归并)
java
// Sharding-JDBC 配置示例
@Bean
public DataSource dataSource() throws SQLException {
return ShardingSphereDataSourceFactory.createDataSource(
createDataSourceMap(),
"md", // 模式
createShardingRuleConfiguration(),
new Properties()
);
}
private ShardingRuleConfiguration createShardingRuleConfiguration() {
ShardingRuleConfiguration config = new ShardingRuleConfiguration();
config.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
// 分库策略:按 user_id 取模
config.setDefaultDatabaseShardingStrategy(
new StandardShardingStrategyConfiguration("user_id", new ModShardingAlgorithm()));
// 分表策略:按 order_id 取模
config.setDefaultTableShardingStrategy(
new StandardShardingStrategyConfiguration("order_id", new ModShardingAlgorithm()));
return config;
}13.2 ★★★★ShardingSphere 的分片策略和分片算法有哪些?如何配置分片规则?分片键如何选择以避免数据倾斜?
📖 点击查看答案
分片策略:
- 标准分片策略(Standard):支持
=、IN、BETWEEN,需配置 PreciseShardingAlgorithm 和可选 RangeShardingAlgorithm - 复合分片策略(Complex):多分片键,实现 ComplexKeysShardingAlgorithm
- 行表达式分片策略(Inline):使用 Groovy 表达式,如
ds${id % 2} - Hint 分片策略(Hint):基于程序 hints 路由,非 SQL 解析
- 不分片策略(None):不进行分片
分片算法:
- 精确分片算法(PreciseShardingAlgorithm):处理
=、IN - 范围分片算法(RangeShardingAlgorithm):处理
BETWEEN、>、< - 复合分片算法(ComplexKeysShardingAlgorithm):多分片键
- Hint 分片算法(HintShardingAlgorithm):编程式路由
java
// 自定义精确分片算法
public class ModShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames,
PreciseShardingValue<Long> shardingValue) {
for (String target : availableTargetNames) {
if (target.endsWith(String.valueOf(shardingValue.getValue() % availableTargetNames.size()))) {
return target;
}
}
throw new UnsupportedOperationException("no target found");
}
}
// 行表达式配置(YAML)
// actualDataNodes: ds${0..1}.t_order_${0..3}
// databaseStrategy:
// inline:
// shardingColumn: user_id
// algorithmExpression: ds${user_id % 2}
// tableStrategy:
// inline:
// shardingColumn: order_id
// algorithmExpression: t_order_${order_id % 4}分片键选择避免数据倾斜的要点:
- 高基数:分片键取值范围要足够大,避免某分片数据过多
- 均匀分布:分片键值分布均匀,避免热点(如按用户ID取模,而非按状态字段)
- 避免单调递增:自增ID作为分片键会导致写入热点(如时间戳),可用 Snowflake 或哈希打散
- 高频查询字段:选择查询条件中高频出现的字段,避免全路由扫描
- 避免热点值:某些用户ID特别活跃(如大V),可加随机后缀或一致性哈希
- 范围查询友好:若需要范围查询,考虑按范围分片或复合分片
- 数据量评估:根据单表容量(建议 1000 万内)和数据增长预估分片数
13.3 ★★★★ShardingSphere 如何处理分布式事务?它支持哪些分布式事务类型(XA 强一致、BASE 柔性事务)?
📖 点击查看答案
事务类型:
| 类型 | 一致性 | 性能 | 实现 | 适用场景 |
|---|---|---|---|---|
| LOCAL 本地事务 | 弱 | 最高 | 不保证跨库一致 | 跨库少的非核心业务 |
| XA 两阶段提交 | 强一致 | 低 | Atomikos/Narayana/Narayana | 数据强一致、并发不高 |
| BASE 柔性事务 | 最终一致 | 高 | Seata AT/TCC、Saga | 高并发业务 |
XA 事务流程:
- 应用发起事务,ShardingSphere 创建 XATransactionManager
- SQL 执行时,各分片资源被 XAResource 包装
- 一阶段:各分片执行 SQL 并 prepare
- 二阶段:所有 prepare 成功则 commit,否则 rollback
- 需配合 XA 事务管理器(Atomikos、Narayana)
BASE 事务(Seata AT):
- 一阶段:拦截 SQL,生成 before/after image 存入 undo_log,本地事务提交
- 二阶段提交:异步删除 undo_log
- 二阶段回滚:根据 undo_log 反向生成补偿 SQL
java
// XA 事务配置
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 开启 XA 事务(注解方式,配合 ShardingSphere 的 XA 模式)
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 跨库操作,ShardingSphere 自动协调 XA
accountMapper.debit(fromId, amount); // ds0
accountMapper.credit(toId, amount); // ds1
}
// YAML 配置
// transaction:
// defaultType: XA
// providerType: Atomikos
// BASE 事务配置(Seata)
// transaction:
// defaultType: BASE
// providerType: SeataXA 缺陷:
- 同步阻塞,性能差
- 单点故障(协调者)
- 数据不一致风险(网络分区时部分 commit)
BASE 优势:
- 一阶段即释放资源,性能高
- 最终一致性适合互联网高并发场景
- 但存在脏读、不可重复读问题(需配合读隔离)
13.4 ★★★分库分表后的常见问题有哪些?如何解决跨库 JOIN、跨库分页、跨库排序、跨库聚合(归并排序、流式处理)?
📖 点击查看答案
分库分表常见问题:
- 跨库 JOIN 困难
- 跨库分页性能差
- 跨库排序、聚合需要归并
- 分布式事务
- 全局唯一 ID 生成
- 数据迁移与扩容
- 跨库事务一致性
跨库 JOIN 解决方案:
- 绑定表:关联表按相同分片键分到同库,避免跨库 JOIN
- 广播表:小表(如字典表)每个库都存一份
- 应用层 JOIN:分别查询后在内存关联
- 冗余字段:反范式,冗余关联字段
- Elasticsearch/数据同步:通过 Canal 同步到 ES 做联合查询
跨库分页问题:
LIMIT 1000000, 10需各库都取 1000010 条归并,性能极差- 解决:禁止深分页、使用游标分页(WHERE id > last_id)、二次查询法、ES 辅助
跨库排序与归并:
java
// ShardingSphere 归并策略
// 1. 流式归并(Stream Merge):各分片结果已排序,多路归并
// - 适用于 ORDER BY + LIMIT,每次只取一条比较
// 2. 内存归并(Memory Merge):所有结果加载到内存排序
// - 适用于 GROUP BY、无索引排序
// 3. 装饰者归并:在流式归并基础上做聚合(MAX/MIN/SUM/COUNT/AVG)
// 跨库聚合示例:SELECT COUNT(*), SUM(amount) FROM t_order
// 各库返回 count_i, sum_i
// 最终 count = sum(count_i), sum = sum(sum_i)
// AVG 需特殊处理:avg = sum(sum_i) / sum(count_i),不能直接求平均
// 跨库分页优化:二次查询法
// 第一查询:各库 LIMIT 0, 10 取出最小值
// 第二查询:根据最小值各库查询 LIMIT min_value, 10
// 合并后取最终结果,减少数据传输分页性能优化要点:
- 避免深分页(超过 1000 页限制)
- 使用游标分页(基于有序主键)
- 借助搜索引擎(ES)做分页
- 预计算热门分页结果
- 使用 ShardingSphere 的流式归并降低内存