ShardingSphere-JDBC探索Hint固定路由

一、为什么会用到固定路由。

当逻辑表不能再以某些字段进行分表,而需要更复杂的逻辑进行分表。或者直接不分而是表固定访问实际表时,都需要用HINT固定路由。

二、用法示例。

先要在 yml里面声明分库分表然后配置固定算法,如果不配置就没法用固定路由。

demo: GitHub - ji602089383/ShardingSphereDemo

以下是配置(一个片段,完整的在demo里)

      sharding:
        key-generators:
          snowflake:
            type: SNOWFLAKE
        tables:
          order:
            actual-data-nodes: ds$->{0..2}.order_$->{0..2}
            database-strategy:
              hint:
                sharding-algorithm-name: myTest              
            table-strategy:
              hint:
                sharding-algorithm-name: hint-default
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
        sharding-algorithms: 
           hint-default: #Hint 行表达式分片算法            
               type: HINT_INLINE
#            props:
#              algorithm-expression: order  #这个配置非必选 当选择这个表达式时,如果要用到ShardingValue值就必须使用 ${value}来获取。 如果不用这个表达式时,那么就是表名就是order + shardingValue值          
           myTest: #Hint 行表达式分片算法            
               type: CLASS_BASED
               props:
                  strategy: hint
                  algorithmClassName: com.turing.order.Ao

配置中声明了 order表 有 ds0 ds1 ds2三个库,每个库中拥有 order_0 order_1 order_2 三个表。

分库是固定策略,使用别名myTest的算法,该算法是一个类型为自定义类的HINT算法,自定义类的路径为com.turing.order.Ao。

分表是固定策略,使用别名hint-default的算法,该算法是一个类型为行表达式的HINT算法,暂时没有配置行表达式。

代码:

OrderEntity orderEntity = new OrderEntity();
orderEntity.setId(id);
orderEntity.setOrderNo("1");
orderEntity.setCreateTime(new Date());
orderEntity.setSuppliersId(id);
//  官方推荐用 try with resource 减少没有关闭清空资源的错误
try (HintManager hintManager = HintManager.getInstance()){
    hintManager.addDatabaseShardingValue("order", "C");    
    hintManager.addDatabaseShardingValue("order", "B");    
    hintManager.addDatabaseShardingValue("order", "F");    
    hintManager.addTableShardingValue("order", 3);    
    hintManager.addTableShardingValue("order", 2);    
    hintManager.addTableShardingValue("order", 1);    
    System.out.println(orderMapper.insertOrder(orderEntity));
}
System.out.println(orderEntity.getId());

上面代码表述了: 要插入一个Order数据,id由用户自己定义, 创建固定路由的管理器,并设置了本次可以找到的数据库路由有3个,对应的ShardingValue值为C,B,F。 设置了本次可以找到的数据表路由有3个,对应的ShardingValue值为3,2,1。

问题

(1)为什么我们在配置addDatabaseShardingValue和addTableShardingValue时第一个参数名为logicTable呢?

HintManager的作用就是给某张表找库的路由和表的路由,所以表为单位,假如现在这个server层,它要插入10张表数据,其中5张表需要走固定路由,那么就配置5张表的不同库路由和表路由策略。

(2)第二个参数ShadingValue有什么作用呢?

它就是我们要进行分片策略时的依据。(如果还不能理解就先简单的理解为按照id分片时的id值)

比如HINT_INLINE算法:HintInlineShardingAlgorithm类中实现了public Collection doSharding(final Collection availableTargetNames, final HintShardingValue> shardingValue)方法。

第一个参数: availableTargetNames 是说,本次我们可以用的目标库或者表的名称。它来源于我们配置文件中actual-data-nodes的值解析后得到数据。按照我们的配置文件,行表达式解析完后得到数据可以是:availableTargetNames = [ds0,ds1,ds2] 或者 availableTargetNames = [order_0,order_1,order_2] 具体是哪个取决于我们这个算法服务于库的路由还是表的路由。

第二个参数:shardingValue,它就是我们配置的 C,B,F  和 3,2,1这些数据。下面给个断点数据给大家看一下

这时别名为hint-default算法的断点,因为它服务于表的路由,所以它得到的ShardingValue是3 2 1。

这是别名为myTest算法的断点,因为它服务于库的路由,所以它得到的ShardingValue是C B F。

以上配置做好后,我们就可以开始测试了。

下面再说HINT的算法:

1、HINT_INLINE

目前HINT的算法 官方只提供了行表达式的。

官方文档中讲,它的行表达式可以不用配置,(下面的举例都是以表路由为准。)

如果不配置行表达式:库和表的路由将是addDatabaseShardingValue或者addTableShardingValue配置的全部路由。举例,按照上面的配置,如果不配置表的算法行表达式则,会路由成:order_0, order_1, order_2

如果配置了行表达式: (1)不需要shardingValue的行表达式。我暂时没有想到这种场景,感觉不用shardingValue值就很奇怪,那干嘛用这个算法呢。(2)需要用ShadingValue的表达式,那表达式中必须用 ${value}来获取ShardingValue值。举例:order_${value % 2} 那按照上面的配置最终的结果就是 order_1, order_0这两个表会被插入数据。在HintInlineShardingAlgorithm算法中,会把我们配置addTableShardingValue值(3,2,1)传入给行表达式的value,实际的计算就是 3 %2, 2%2, 1%2最终得到 1, 0。就是order_1, order_0。   然后这个比较简单的配置,根据实际场景,这个固定路由行表达会相当复杂。

2、CLASS_BASED

这个就是需要我们自己定义算法。

下面是我写非常简单的一个算法,就是固定路由到ds0上,这个是服务于库路由的算法。当然大家可以将表和库的路由规则写到一个算法里。

非常复杂的算法我就不写了,大家有兴趣可根据情况自己写写测试一下。

扩展:

hintManager.setDatabaseShardingValue(0);

这个方法的使用:

该方法调用后会将HintManager中的databaseShardingValues 和 tableShardingValues 都清空,然后设置databaseShardingOnly为true。

并且这个方法的调用后,不能再调用 addDatabaseShardingValue和addTableShardingValue 否则,会导致该方法调用的结果失效,数据被重置。

那么一起看下源码对该方法调用后怎么处理的。这里我从路由引擎开始讲。如果有兴趣的可以更深入的看。

大家去shardingsphere-shading-core依赖中的org.apache.shardingsphere.sharding.route.engine.type.standard包下ShardingStandardRoutingEngine类(标准分片路由引擎, 还有很多种引擎,至于是什么规则使用了这个引擎,可以去看org.apache.shardingsphere.sharding.route.engine.type.ShardingRouteEngineFactory#newInstance方法)。

下面理一下这个类中关键方法的调用:

(1)开始调用route

(2)然后选择数据节点,getDataNodes

(3)isRoutingByHint判定了本次路由属于HINT,并调用了routByHint方法

rountByHint里它调用了两个方法,这两个方法就是从我们再Server层设置的HintManager中获取库的ShardingValue和表的ShardingValue。

getDatabaseShadingValuesFromHint方法中,先判定了我们是采用了SQL注释Hint的方式,还是手动HintManager的方式,很显然我们采用的是HintManager,于是,判定HintManager中的databaseShadingOnly是否为true,hintManager.setDatabaseShardingValue(0)方法我们可以知道databaseShadingOnly是true,最后传递给getShadingConditions方法的参数为 集合=[0]。

getShadingConditions最后经过计算得到 一个对象ListShardingConditionValue,该对象中columnName="", tableName="order", value= [0]

同理的大家可以自己看下getTableShadingValuesFromHint()方法。

(4)进入方法rout0

第一圈中的两个数就是从HintManager得到的数据。

(5)进入routDataSources方法

这里关键。它调用了库分片策略进行分片。当我们进入org.apache.shardingsphere.sharding.route.strategy.type.hint.HintShardingStrategy#doSharding

分片策略的doSharding方法,它获取ShardingValue值,大家都知道它是一个 对象ListShardingConditionValue,该对象中columnName="", tableName="order", value= [0]。

shadingAlgorithm这个对象就是分片的算法。我们配置文件中配置的固定分片算法是CLASS_BASED类型。

于是找到org.apache.shardingsphere.sharding.algorithm.sharding.classbased.ClassBasedShardingAlgorithm类。这里的hintShardingAlgorithm其实就是我们自己实现Ao算法。于是就回到了我们自己的Ao类中。

Ao类中我们写死返回ds0。于是上面的doShading返回的结果就是 [ds0]

走到这里我们,一步一步返回数据到route0方法,并进入for,因为就一个库源,直接进入routTablesf方法查看吧。

(6) routeTables方法

它主要就是说。先获取本次选取的库中的真实的表。然后判定,表的ShardingValue是否为空,根据前面的设置我们知道HintManager中是没有设置表的SharedingValue的,所以 routedTables拿到的是全部的真实表。然后循环创建DataNode对象 就是 ds0.order_0 ds0.order_1 ds0.order_2。

开始返回数据 最终给到 最开始 route方法的数据 Collection dataNodes就是ds0.order_0 ds0.order_1 ds0.order_2 这三个数据。那后面的结果我就知道它会往这三个表中插入数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值