ShardingJDBC强制路由详解与实战

强制路由详解与实战

1 强制路由介绍

https://shardingsphere.apache.org/document/4.1.0/cn/manual/sharding-jdbc/usage/hint/

在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。

Hint使用场景:

  • 数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中
  • 读写分离操作,如果强制在主库进行某些数据操作

2 强制路由的使用

基于 Hint 进行强制路由的设计和开发过程需要遵循一定的约定,同时,ShardingSphere 也提供了专门的 HintManager 来简化强制路由的开发过程.

2.1 环境准备

  1. shardingjdbc0shardingjdbc1中创建 t_course表.

    sql 复制代码
    CREATE TABLE `t_course` (
      `cid` bigint(20) NOT NULL,
      `user_id` bigint(20) DEFAULT NULL,
      `corder_no` bigint(20) DEFAULT NULL,
      `cname` varchar(50) DEFAULT NULL,
      `brief` varchar(50) DEFAULT NULL,
      `price` double DEFAULT NULL,
      `status` int(11) DEFAULT NULL,
      PRIMARY KEY (`cid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.2 代码编写

java 复制代码
@TableName("t_course")
@Data
@ToString
public class Course implements Serializable {

    @TableId(type = IdType.ASSIGN_ID)
    private Long cid;

    private Long userId;

    private Long corderNo;

    private String cname;

    private String brief;

    private double price;

    private int status;
}

CourseMapper

java 复制代码
@Repository
public interface CourseMapper extends BaseMapper<Course> {
}

自定义MyHintShardingAlgorithm类

在该类中编写分库或分表路由策略,实现HintShardingAlgorithm接口,重写doSharding方法

java 复制代码
// 泛型Long表示传入的参数是Long类型
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    public MyHintShardingAlgorithm() {
        System.out.println("MyHintShardingAlgorithm 被创建了!");
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                         HintShardingValue<Long> shardingValue) {
        Collection<String> result = new ArrayList<>();
        for (String target : availableTargetNames) {
            for (Long value : shardingValue.getValues()) {
                if (target.endsWith(String.valueOf(value % 2))) {
                    result.add(target);
                }
            }
        }
        return result;
    }

    @Override
    public void init() {
    }

    @Override
    public String getType() {
        // CLASS_BASED 模式下不会用到此 type,但实现返回值无妨
        return "MY_HINT";
    }
}

参数解析:

参数 含义
availableTargetNames 当前所有可用的数据节点名称,例如:["db0", "db1"]["t_course_0", "t_course_1"]
shardingValue 通过 hintManager.addDatabaseShardingValue(...)addTableShardingValue(...) 传入的值
Collection<String> 实际要路由到的目标

2.3 配置文件

application.properties

properties 复制代码
# 启用 debug 日志
logging.level.org.apache.shardingsphere=DEBUG
# 应用名称
spring.application.name=shardingsphere-jdbc-table

# 打印 SQL
spring.shardingsphere.props.sql-show=true

# 定义多个数据源
spring.shardingsphere.datasource.names=db0,db1

# 数据源1
spring.shardingsphere.datasource.db0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db0.jdbc-url=jdbc:mysql://192.168.116.128:3306/shardingjdbc0?characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.db0.username=root
spring.shardingsphere.datasource.db0.password=123456

# 数据源2
spring.shardingsphere.datasource.db1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.jdbc-url=jdbc:mysql://192.168.116.129:3306/shardingjdbc1?characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=123456

# 默认数据源(建议保留)
spring.shardingsphere.rules.sharding.default-data-source-name=db0

# t_course 表实际数据节点
spring.shardingsphere.rules.sharding.tables.t_course.actual-data-nodes=db${0..1}.t_course_${0..1}



# 数据库分片策略 - Hint
spring.shardingsphere.rules.sharding.tables.t_course.database-strategy.hint.sharding-algorithm-name=myHint

# 表分片策略 - Hint
spring.shardingsphere.rules.sharding.tables.t_course.table-strategy.hint.sharding-algorithm-name=myHint

# 分片算法定义 - MY_HINT
spring.shardingsphere.rules.sharding.sharding-algorithms.myHint.type=CLASS_BASED
spring.shardingsphere.rules.sharding.sharding-algorithms.myHint.props.strategy=HINT
spring.shardingsphere.rules.sharding.sharding-algorithms.myHint.props.algorithmClassName=com.zhp.hint.MyHintShardingAlgorithm

2.4 强制路由到库到表测试

通过设置addDatabaseShardingValue 决定路由到哪个数据库,addTableShardingValue决定路由到哪张表

java 复制代码
@Test
    public void testHintInsert(){
        HintManager hintManager = HintManager.getInstance();
        hintManager.addDatabaseShardingValue("db", 1L);
        hintManager.addTableShardingValue("t_course", 0L);
        for (int i = 1; i < 9; i++) {
            Course course = new Course();
            course.setCid(Long.parseLong(String.valueOf(i)));
            course.setUserId(1001L+i);
            course.setCname("Java经典面试题讲解");
            course.setBrief("课程涵盖目前最容易被问到的10000道Java面试题");
            course.setPrice(100.0);
            course.setStatus(1);
            courseMapper.insert(course);
        }
    }

2.5 强制路由到库到表查询测试

java 复制代码
//测试查询
    @Test
    public void testHintSelectTable() {
        HintManager hintManager = HintManager.getInstance();
        //强制路由到db1数据库
        hintManager.addDatabaseShardingValue("db", 1L);
        //强制路由到t_course_1表
        hintManager.addTableShardingValue("t_course",0L);
        List<Course> courses = courseMapper.selectList(null);
        courses.forEach(System.out::println);
    }

2.6 强制路由走主库查询测试

在读写分离结构中,为了避免主从同步数据延迟及时获取刚添加或更新的数据,可以采用强制路由走主库查询实时数据,使用hintManager.setMasterRouteOnly设置主库路由即可。

  1. 配置文件
sql 复制代码
# 应用名称
spring.application.name=sharding-jdbc-hint01

# 定义多个数据源
spring.shardingsphere.datasource.names = m1,s1

#数据源1
spring.shardingsphere.datasource.db0.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db0.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db0.url = jdbc:mysql://192.168.116.128:3306/shardingjdbc0?characterEncoding=UTF-8&useSSL=false
spring.shardingsphere.datasource.db0.username = root
spring.shardingsphere.datasource.db0.password = 123456

#数据源2
spring.shardingsphere.datasource.db1.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db1.url = jdbc:mysql://192.168.116.129:3306/shardingjdbc1?characterEncoding=UTF-8&useSSL=false
spring.shardingsphere.datasource.db1.username = root
spring.shardingsphere.datasource.db1.password = 123456

#主库与从库的信息
spring.shardingsphere.sharding.master-slave-rules.ms1.master-data-source-name=m1
spring.shardingsphere.sharding.master-slave-rules.ms1.slave-data-source-names=s1

#配置数据节点
spring.shardingsphere.sharding.tables.products.actual-data-nodes = ms1.products

# 打印SQl
spring.shardingsphere.props.sql-show=true
  1. 测试
java 复制代码
//强制路由走主库
@Test
public void testHintReadTableToMaster() {
    HintManager hintManager = HintManager.getInstance();
    hintManager.setMasterRouteOnly();

    List<Products> products = productsMapper.selectList(null);
    products.forEach(System.out::println);
}

2.7 SQL执行流程剖析

ShardingSphere 3个产品的数据分片功能主要流程是完全一致的,如下图所示。

  • SQL解析

    SQL解析分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。

    Sharding-JDBC采用不同的解析器对SQL进行解析,解析器类型如下:

    • MySQL解析器
    • Oracle解析器
    • SQLServer解析器
    • PostgreSQL解析器
    • 默认SQL解析器
  • 查询优化

    负责合并和优化分片条件,如OR等。

  • SQL路由

    根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

  • SQL改写

    将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。

  • SQL执行

    通过多线程执行器异步执行SQL。

  • 结果归并

    将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

相关推荐
Awkwardx1 小时前
MySQL数据库—MySQL复合查询
数据库·mysql
亮子AI1 小时前
【MySQL】node.js 如何批量更新数据?
数据库·mysql·node.js
摇滚侠1 小时前
两句话理解 ElasticSearch 搜索引擎数据库的作用
数据库·mysql·搜索引擎
哈里谢顿10 小时前
记录一次sql优化记录
mysql
数据大魔方10 小时前
【期货量化实战】日内动量策略:顺势而为的短线交易法(Python源码)
开发语言·数据库·python·mysql·算法·github·程序员创富
Chasing Aurora11 小时前
数据库连接+查询优化
数据库·sql·mysql·prompt·约束
萧曵 丶12 小时前
Next-Key Lock、记录锁、间隙锁浅谈
数据库·sql·mysql·mvcc·可重复读·幻读
莳花微语13 小时前
记录一次OGG进程abended,报错OGG-01431、OGG-01003、OGG-01151、OGG-01296问题的处理
数据库·sql·mysql
萧曵 丶14 小时前
MySQL三大日志系统浅谈
数据库·sql·mysql
麦聪聊数据16 小时前
MySQL 性能调优:从EXPLAIN到JSON索引优化
数据库·sql·mysql·安全·json