参考代码工程(2-standard)
第三节使用springboot搭建sharding环境时候以最简单的inline策略为演示,发现inline只支持最简单的等于查询。对于范围查询不支持(表通常要结合时间最范围查询)
此时要引用标准分片策略(standard)
standard策略可以看到配置信息一共3个参数
#分片主键
sharding-column=
# 精准分片算法-》指定我们实现类
precise-algorithm-class-name=
# 范围查询算法-》指定我们实现类
range-algorithm-class-name=
使用yaml修改配置类成以下
# 单库分表 配置
spring:shardingsphere:datasource:# 配置数据库名称 相当于给数据源取别名(可以配置多个库,以逗号隔开)names: m1# 配置具体数据库连接信息m1:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driver# 配置 数据库 testurl: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTCusername: rootpassword: root# 分片策略sharding:# 配置不同表的 分片策略tables:# 配置 具体的 逻辑表的 分片策略t_order:# t_order 订单表的 主键规则keyGenerator:# 主键列column: order_id# 主键生成规则 (SNOWFLAKE 雪花算法 生成分布式唯一ID)type: SNOWFLAKE# 未知作用# props:# worker:# id: 1# 配置 t_order 订单表的 具体数据库物理表的映射关系 表达式actualDataNodes: m1.t_order_$->{1..2}# 表策略tableStrategy:standard:# 分片列sharding-column: order_id# 范围分片算法类名称,用于 范围查询 可选。该类需实现 RangeShardingAlgorithm 接口并提供无参数的构造器range-algorithm-class-name: com.wnn.sd.algorithm.MyRangeShardingAlgorithm# 精确分片算法类名称,用于 = 和 IN。该类需实现 PreciseShardingAlgorithm 接口并提供无参数的构造器precise-algorithm-class-name: com.wnn.sd.algorithm.MyPreciseShardingAlgorithm# 配置是否打印SQLprops:sql.show: true
创建一个包“com.wnn.sd.algorithm”,首先实现精准分片算法PreciseShardingAlgorithm,所谓精准分片算法,可以参考inline算法一样实现一个对订单表主键取摸2的算法
package com.wnn.sd.algorithm;import java.math.BigInteger;
import java.util.Collection;import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;import lombok.extern.slf4j.Slf4j;/*** @Description: 精确匹配查询,需要实现PreciseShardingAlgorithm,可以实现对 `=`以及`in`的查询**/
@Slf4j
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm {/*** 精确匹配查询** @param tbNames* 数据库中所有的事实表* @param shardingValue* 分片相关信息* @return 返回匹配的数据源*/@Overridepublic String doSharding(Collection tbNames, PreciseShardingValue shardingValue) {// 获取-打印逻辑表String logicTableName = shardingValue.getLogicTableName();log.info("logicTableName = {}", logicTableName);// 主键值转换BigInteger做计算BigInteger orderId = BigInteger.valueOf(shardingValue.getValue());// 取摸算法%2+1BigInteger tabelNuber = (orderId.mod(new BigInteger("2"))).add(new BigInteger("1"));// 打印实际路由后的表String actTableName = logicTableName + "_" + tabelNuber;log.info("actTableName= {}", actTableName);// 判断算法的结果是否在数据库实际表列表中if (tbNames.contains(logicTableName + "_" + tabelNuber)) {return actTableName;}throw new RuntimeException("表不存在");}
}
把t_order_1和t_order_2清除,使用Test类的add()方法(参考上一节Test类)给数据库添加10条数据,结果保持和inline一致。
/*** 添加数据*/@Testpublic void addOrder() {for (int i = 0; i < 10; i++) {Order order = new Order();order.setOrderName("wn" + i);orderMapper.insert(order);}}/*** 等于查询 ID基于表2*/@Testpublic void queryByEQ() {List orders =orderMapper.selectList(new LambdaQueryWrapper().eq(Order::getOrderId, 1629805480188895233L));orders.forEach(o -> System.out.println(o));}
可以看到插入日志中输出了@Slf4j 下 log.info()信息,说明新数据,sharding能够根据我们的配置信息,加载我们实现的算法类
add()
2023-03-05 15:06:52.819 INFO 23220 --- [ main] c.w.s.a.MyPreciseShardingAlgorithm : logicTableName = t_order
2023-03-05 15:06:52.819 INFO 23220 --- [ main] c.w.s.a.MyPreciseShardingAlgorithm : actTableName= t_order_2
queryByEQ() ,路由查询结果正确
2023-03-05 15:09:47.354 INFO 35236 --- [ main] c.w.s.a.MyPreciseShardingAlgorithm : logicTableName = t_order
2023-03-05 15:09:47.355 INFO 35236 --- [ main] c.w.s.a.MyPreciseShardingAlgorithm : actTableName= t_order_2
2023-03-05 15:09:47.374 INFO 35236 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT order_id,2023-03-05 15:09:47.375 INFO 35236 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT order_id,order_name,order_type,order_desc,create_user_id,create_user_name,create_time FROM t_order_2 WHERE (order_id = ?) ::: [1629805480188895233]
再实现范围查询算法MyRangeShardingAlgorithm类,该类实现RangeShardingAlgorithm类,此时从方法shardingValue中拿到SQL语句中between的上限和下限值,因为没有具体业务分片算法暂时列为//TODO,返回结果为全表(一共2张表)测试通过RangeShardingAlgorithm实现类doSharding()方法是否能够执行between查询
package com.wnn.sd.algorithm;import java.util.Arrays;
import java.util.Collection;import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;import lombok.extern.slf4j.Slf4j;/*** 实现范围查询逻辑**/
@Slf4j
public class MyRangeShardingAlgorithm implements RangeShardingAlgorithm {@Overridepublic Collection doSharding(Collection availableTargetNames,RangeShardingValue shardingValue) {Long upperValue = shardingValue.getValueRange().upperEndpoint();// 上限Long lowerValue = shardingValue.getValueRange().lowerEndpoint();// 下限//TODOString logicTableName = shardingValue.getLogicTableName();log.info("MyRangeShardingAlgorithm + logicTableName {}", logicTableName);return Arrays.asList(logicTableName + "_1", logicTableName + "_2");}
}
可以看到打印输出结果,不像使用inline那样报错,而是通过MyRangeShardingAlgorithm算法返回的表名执行SQL
2023-03-05 15:25:48.009 WARN 12852 --- [ main] c.b.m.c.t.support.ReflectLambdaMeta : Unable to make field private final java.lang.Class java.lang.invoke.SerializedLambda.capturingClass accessible: module java.base does not "opens java.lang.invoke" to unnamed module @3d012ddd
2023-03-05 15:25:48.387 INFO 12852 --- [ main] c.w.s.a.MyRangeShardingAlgorithm : MyRangeShardingAlgorithm + logicTableName t_order2023-03-05 15:25:48.418 INFO 12852 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT order_id,order_name,order_type,order_desc,create_user_id,create_user_name,create_time FROM t_order_1 WHERE (order_id BETWEEN ? AND ?) ::: [1629805480188895233, 1629805480214061057]
2023-03-05 15:25:48.418 INFO 12852 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT order_id,order_name,order_type,order_desc,create_user_id,create_user_name,create_time FROM t_order_2 WHERE (order_id BETWEEN ? AND ?) ::: [1629805480188895233, 1629805480214061057]
由此可见standard标准分片算法,实现相比inline繁杂一点。一般使用standard策略是为了搭配范围分片算法使用。