在一些应用场景中,分片条件并不在SQL中,而是由外部的业务逻辑决定。因此,需要提供一种通过在外部业务代码中指定路由配置的一种方式。
这种方式在 ShardingJDBC 中称为 hint,如果用 hint 做强制路由,那么 sql 将无视原有的分片逻辑,直接将请求路由到指定的数据库节点上进行操作。
1 应用场景
hint 使用场景:
数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中;
读写分离操作,如果强制在主库进行某些数据操作。
2 应用过程
-
编写分库或分表路由策略,实现 HintShardingAlgorithm 接口
-
在配置文件中指定分库或分表策略:
可以设置强制路由库;
或者同时设置强制路由库及强制路由表。
- 在代码执行查询前使用 HintManager 指定执行策略值
在读写分离结构中,为了避免主从同步数据延迟,及时获取刚刚添加或更新的数据,可以采用强制路由走主库,查询实时数据,使用 hintManager.setMasterRouteOnly 设置主库路由即可。
3 应用实战
3.1 创建配置文件
创建用于做强制路由的配置文件 application-hint-database.properties
配置数据库节点信息:
#datasource
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://192.168.137.3:3366/orders?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://192.168.137.3:3377/orders?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
在其中添加强制路由配置:
- 为数据表 province 配置强制路由信息
- 路由类的位置在工程中的全路径信息 com.myjdbc.hint.MyHintAlgorithm
spring.shardingsphere.sharding.tables.province.database-strategy.hint.algorithm-class-name=com.myjdbc.hint.MyHintAlgorithm
3.2 修改主配置文件
修改主配置文件 application.properties
将配置项 spring.profiles.active 指定到新建的配置文件 application-hint-database.properties
spring.profiles.active=hint-database
3.3 创建分库分表的路由策略
在创建的分库分表路由策略类中,实现 HintShardingAlgorithm 接口,接口后的参数为对应数据表的主键类型的封装类。
实现该接口中的方法 doSharding,在其中定义路由规则。
package com.myjdbc.hint;
import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
import java.util.ArrayList;
import java.util.Collection;
public class MyHintAlgorithm implements HintShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Integer> hintShardingValue) {
ArrayList<String> result = new ArrayList<>();
for(String str : collection){
for(Integer value: hintShardingValue.getValues()){
if(str.endsWith(String.valueOf(value % 2))){
result.add(str);
}
}
}
return result;
}
}
3.4 创建测试类
在测试类中,使用 HintManager 指定执行策略值:
- manager.setDatabaseShardingValue(1); 将调用上边定义的分表路由策略 MyHintAlgorithm,计算 ds${1 % 2} = ds1 该次操作将被强制路由到数据库ds1节点;
- manager.setDatabaseShardingValue(0); 将调用上边定义的分表路由策略 MyHintAlgorithm,计算 ds${0 % 2} = ds0 该次操作将被强制路由到数据库ds0节点。
package com.myjdbc;
import com.myjdbc.entity.Province;
import com.myjdbc.repository.ProvinceRepository;
import org.apache.shardingsphere.api.hint.HintManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShardingJdbc.class)
public class TestHint {
@Resource
private ProvinceRepository provinceRepository;
@Test
public void TestQuery(){
HintManager manager = HintManager.getInstance();
// 强制路由到节点 ds${xx%2}
manager.setDatabaseShardingValue(1);
List<Province> all = provinceRepository.findAll();
all.forEach(System.out::println);
// 强制路由到节点 ds${xx%2}
manager.setDatabaseShardingValue(0);
all = provinceRepository.findAll();
all.forEach(System.out::println);
}
}
运行程序,查看结果:
第一次路由到ds1,第二次查询被路由到ds0