(三)、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设置主库路由即可。

测试插入数据,结果如下:

 也可以查看数据库

 测试查询

 这里演示的只是分库,分库分表的测试可以自行进行

分库分表参考

SpringBoot2.0.3.RELEASE+sharding-jdbc4.1.0+mybatis-plus3.4.1+druid1.1.22 快速搭建分库分表 - 劈天造陆 - 博客园 (cnblogs.com) 

完整代码及sql脚本在:

https://gitee.com/xiaorenwu_dashije/sharding-sphere-demo.git

posted @ 2021-11-12 14:46  劈天造陆  阅读(3525)  评论(1编辑  收藏  举报