(三)、Sharding-JDBC强制路由
在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。
HintManager主要使用ThreadLocal管理分片键信息,进行hint强制路由。在代码中向HintManager添加的配置信息只能在当前线程内有效。
Hint使用场景:
- 数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中
- 读写分离操作,如果强制在主库进行某些数据操作
Hint使用过程:
- 编写分库或分表路由策略,实现HintShardingAlgorithm接口
package com.qjc.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; /** * @ClassName: MyHintShardingAlgorithm * @Description: Hint使用场景: * 1、数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中 * 2、读写分离操作,如果强制在主库进行某些数据操作 * @Author: qjc * @Date: 2021/11/10 6:17 下午 */ public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Long> shardingValue) { Collection<String> result = new ArrayList<>(); for (String each : availableTargetNames) { for (Long value : shardingValue.getValues()) { if (each.endsWith(String.valueOf(value % 2))) { result.add(each); } } } return result; } }
- 在配置文件指定分库或分表策略
# 命名数据源 这个是自定义的 spring.shardingsphere.datasource.names=ds-0,ds-1 # 配置数据源ds-0 spring.shardingsphere.datasource.ds-0.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.ds-0.driverClassName=com.mysql.jdbc.Driver spring.shardingsphere.datasource.ds-0.url=jdbc:mysql://127.0.0.1:3306/ds-0?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true spring.shardingsphere.datasource.ds-0.username=root spring.shardingsphere.datasource.ds-0.password=123456 # 配置数据源ds-1 spring.shardingsphere.datasource.ds-1.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.ds-1.driverClassName=com.mysql.jdbc.Driver spring.shardingsphere.datasource.ds-1.url=jdbc:mysql://127.0.0.1:3306/ds-1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true spring.shardingsphere.datasource.ds-1.username=root spring.shardingsphere.datasource.ds-1.password=123456 # 配置默认数据源ds-0 spring.shardingsphere.sharding.default-data-source-name=ds-0 # Hint强制路由 # 使用t_city表测试强制路由到库 spring.shardingsphere.sharding.tables.t_city.database-strategy.hint.algorithm-class-name=com.qjc.hint.MyHintShardingAlgorithm # 使用t_order表测试强制路由到库和表 spring.shardingsphere.sharding.tables.t_order.database-strategy.hint.algorithm-class-name=com.qjc.hint.MyHintShardingAlgorithm spring.shardingsphere.sharding.tables.t_order.table-strategy.hint.algorithm-class-name=com.qjc.hint.MyHintShardingAlgorithm spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..1}
- 在代码执行查询前使用HintManager指定执行策略值
package com.qjc; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.qjc.entity.TCity; import com.qjc.entity.TOrder; import com.qjc.mapper.TCityMapper; import com.qjc.mapper.TOrderMapper; import org.apache.shardingsphere.api.hint.HintManager; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Repeat; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest(classes = ShardingSphereDemoApplication.class) public class TestHintAlgorithm { @Resource private TCityMapper cityMapper; @Autowired private TOrderMapper orderMapper; @Test public void testAdd() { HintManager hintManager = HintManager.getInstance(); // 插入10条数据,通过MyHintShardingAlgorithm可知,强制路由到ds-${value%2} hintManager.setDatabaseShardingValue(1L); for (int i = 0; i < 10; i++) { TCity city = new TCity(); city.setName("商丘市" + i); city.setProvince("河南省" + i); cityMapper.insert(city); } } @Test public void test1() { HintManager hintManager = HintManager.getInstance(); // 通过MyHintShardingAlgorithm可知,强制路由到ds-${value%2} // hintManager.setDatabaseShardingValue(1L); hintManager.addDatabaseShardingValue("t_city", 1L); List<TCity> list = cityMapper.selectList(new QueryWrapper<>()); list.forEach(city -> { System.out.println(city.getId() + " " + city.getName() + " " + city.getProvince()); }); } @Test public void test2() { HintManager hintManager = HintManager.getInstance(); // 强制路由到库ds-1 hintManager.addDatabaseShardingValue("t_order", 1L); // 强制路由到表t_order_1 hintManager.addTableShardingValue("t_order", 1L); List<TOrder> list = orderMapper.selectList(new QueryWrapper<>()); list.forEach(order -> { System.out.println(order.getOrderId() + " " + order.getOrderNo()); }); } }
在读写分离结构中,为了避免主从同步数据延迟及时获取刚添加或更新的数据,可以采用强制路由走主库查询实时数据,使用hintManager.setMasterRouteOnly设置主库路由即可。
测试插入数据,结果如下:
也可以查看数据库
测试查询
这里演示的只是分库,分库分表的测试可以自行进行
分库分表参考
完整代码及sql脚本在:
https://gitee.com/xiaorenwu_dashije/sharding-sphere-demo.git
劈天造陆,开辟属于自己的天地!!!与君共勉