上源码:
@Getter
@Setter
public class YamlShardingStrategyConfiguration implements YamlConfiguration {
private YamlStandardShardingStrategyConfiguration standard;
private YamlComplexShardingStrategyConfiguration complex;
private YamlHintShardingStrategyConfiguration hint;
private YamlInlineShardingStrategyConfiguration inline;
private YamlNoneShardingStrategyConfiguration none;
}
即在yml配置文件中对应分库分表策略:
每一种不同的分片策略各自的属性值不同
1、inline(行表达式)
使用很简单,通过书写表达式即可完成(groovy表达式)
@Getter
@Setter
public final class YamlInlineShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration {
private String shardingColumn;
private String algorithmExpression;
}
与配置文件对应
2、standard
这里用分表来举例
指定分片键,自定义分片策略算法
@Getter
@Setter
public final class YamlStandardShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration {
private String shardingColumn;
private String preciseAlgorithmClassName;
private String rangeAlgorithmClassName;
}
preciseAlgorithmClassName
:精准算法(sql语句中有=、in) 此必须实现
rangeAlgorithmClassName
:范围算法(between and)
preciseAlgorithmClassName使用场景:以分片键作为查询字段并使用=、in
public interface PreciseShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
/**
* Sharding.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding result for data source or table's name
*/
String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue);
}
首先准备配置
sharding:
tables:
#逻辑表名
user:
actual-data-nodes: db1.table_$->{[1,2]}
#actual-data-nodes: db.table_1,db.table_2
key-generator:
column: id #自定填充字段
type: SNOWFLAKE #雪花算法 UUID、SNOWFLAKE两种(实现ShardingKeyGenerator接口)
table-strategy: #分表策略
standard:
shardingColumn: id
preciseAlgorithmClassName: com.example.sharding_jdbc.standard.MyPreciseShardingAlgorithm
#rangeAlgorithmClassName:
自定义的PreciseShardingAlgorithm:
打上断点:测试插入一条数据运行
User user = new User();
user.setName("sam");
user.setAge(22);
user.setSex(1);
userMapper.insert(user);
doSharding方法的参数:
1、availableTargetNames:所有的数据存储节点
2、shardingValue:分片键值
这里所有存储节点与配置文件中的对应,配置的分片键就是id,这里也拿到。
doSharding方法的参数:
方法的返回值就是最终选择的表
实现一个偶数存table_1、奇数id存table_2
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
for (String targetName : availableTargetNames) {
//偶数存table_1、基数存table_2
String value = shardingValue.getValue() % availableTargetNames.size() +1+ "";
if(targetName.endsWith(value)){
return targetName;
}
}
throw new RuntimeException("分片策略出错");
}
}
运行测试结果:
Actual SQL: db1 ::: INSERT INTO table_2 (id, age, name, sex) VALUES (?, ?, ?, ?) ::: [1475333493534035969, 22, sam, 1]
rangeAlgorithmClassName
测试范围查询:使用场景sql语句中使用分片键做查询并使用between and做范围查询
List<User> users = userMapper.selectList(new QueryWrapper<User>().gt("age", 20));
如果不实现RangeShardingAlgorithm,默认会从所以的数据节点中查询然后合并结果
: Actual SQL: db1 ::: SELECT id,age,name,sex FROM table_1
WHERE (age > ?) ::: [20]
Actual SQL: db1 ::: SELECT id,age,name,sex FROM table_2
WHERE (age > ?) ::: [20]
自定义实现RangeShardingAlgorithm
public class MyRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
Set<String> list = new HashSet<>();
Long lower = shardingValue.getValueRange().lowerEndpoint();
Long upper = shardingValue.getValueRange().upperEndpoint();
list.add("table_1");
return list;
}
}
方法参数
1、availableTargetNames
所有实际数据节点
2、shardingValue
范围值
方法返回值
返回查询的最终数据节点集合
这里我指定返回单一数据的集合(可自定义逻辑返回集合),那么最终查询只会在这一个数据节点上查询
查询语句:要以对应分片键作为查询字段
List<User> users = userMapper.selectList(new QueryWrapper<User>()
.between("id", 1475333493534035969L,1475333493534035979L));
日志
Actual SQL: db1 ::: SELECT id,age,name,sex FROM table_1
WHERE (id BETWEEN ? AND ?) ::: [1475333493534035969, 1475333493534035979]
最终只在指定的数据节点上做查询
3、hint
不需要在配置文件中指定分片键,在代码中定义最终选择的库和表
@Getter
@Setter
public final class YamlHintShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration {
private String algorithmClassName;
}
配置文件:分库分表使用同一个实现HintShardingAlgorithm的类
tables:
#逻辑表名
user:
actual-data-nodes: db1.table_$->{[1,2]}
database-strategy:
hint:
algorithm-class-name: com.example.sharding_jdbc.hint.MyHintShardingAlgorithm
table-strategy: #分表策略
hint:
algorithmClassName: com.example.sharding_jdbc.hint.MyHintShardingAlgorithm
实现HintShardingAlgorithm接口
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Integer> shardingValue) {
ArrayList<String> list = new ArrayList<String>() ;
for (String targetName : availableTargetNames) {
if(shardingValue.getValues().contains(targetName)){
list.add(targetName);//选择库、表
break;
}
}
return list;
}
}
在调用sql语句之前要使用hintmannager设置分库分表的分片键,因为hintmannager是由ThreadLocal管理,每次使用记得调用clear()从threadLocal中清除
public static HintManager getInstance() {
Preconditions.checkState(null == HINT_MANAGER_HOLDER.get(), "Hint has previous value, please clear first.");
HintManager result = new HintManager();
HINT_MANAGER_HOLDER.set(result);
return result;
}
测试
HintManager.clear();
HintManager hintManager = HintManager.getInstance();
hintManager.addTableShardingValue("user","table_2");
hintManager.addDatabaseShardingValue("user","db1");
// hintManager.setDatabaseShardingValue("db1");
List<User> users = userMapper.selectList(new QueryWrapper<User>().select("*"));
addTableShardingValue("user","table_2")
:参数一是要指定分表的逻辑表名(配置文件中配置的),参数二设置最终选择的实际数据表
addDatabaseShardingValue("user","db1")
:参数一是要指定分表的逻辑表名(配置文件中配置的),参数二设置最终选择的实际数据库
setDatabaseShardingValue
:之分库不分表
待定…
4、complex
可以配置多个分片键
@Getter
@Setter
public final class YamlComplexShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration {
private String shardingColumns;
private String algorithmClassName;
}
自定义策略实现ComplexKeysShardingAlgorithm接口
public interface ComplexKeysShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
/**
* Sharding.
*
* @param availableTargetNames 实际存储数据节点
* @param shardingValue 分片键值
* @return 最终要操作的数据节点
*/
Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<T> shardingValue);
}
测试查询语句,这里只使用到配置的分片键中的id
List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("id", 1L));
shardingValue参数只会包含sql语句中使用到的id
测试使用两个分片键
List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("id", 1L)
.eq("sex",0));
最后按照自己的逻辑返回最终要查询的数据节点集合
5、none
即不使用分片策略
注意配置,因为
public final class YamlNoneShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration {
}
none对应的类没有任何属性,需要自定义再加一层才能解析成功