ShardingJDBC 11_强制路由

792 阅读2分钟

在一些应用场景中,分片条件并不在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

image.png