Hint分片策略
这种分片算法不用在配置文件中指定分片键
,分片值也不再从SQL
中解析,而是从外部手动指定分片库,让SQL
在指定的库表中执行。
先创建一个自定义的CustomDBHintShardingAlgorithm
类,让它实现HintShardingAlgorithm<Long>
接口。
package com.itechthink.strategy;
import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
import java.util.ArrayList;
import java.util.Collection;
/**
* 自定义分库分表策略:Hint分片算法
*
*/
public class CustomHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
/**
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param hitShardingValue 分片属性
* logicTableName 逻辑表名
* columnName 分片健,在Hint分片策略下值为""
* value 从SQL中解析出的分片健的值,用于取模判断
* HintShardingAlgorithm 不再从SQL解析中获取值,而是直接通过hintManager.addTableShardingValue("t_order", 1)参数进行指定
*/
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, HintShardingValue<Long> hitShardingValue) {
Collection<String> result = new ArrayList<>();
for (String datasourceName : dataSourceNames) {
for (Long shardingValue : hitShardingValue.getValues()) {
String value = String.valueOf(shardingValue % dataSourceNames.size());
if (datasourceName.endsWith(value)) {
result.add(datasourceName);
}
}
}
return result;
}
}
然后修改配置文件application.properties
,注释掉其他策略,仅保留数据节点并指定Hint分片算法
。
spring.application.name=mysql-sharding-jdbc
server.port=8080
# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true
# 数据源
spring.shardingsphere.datasource.names=ds0,ds1,ds2
# 第一个数据源
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://172.16.185.176:3306/itechthink_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&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://172.16.185.176:3306/itechthink_order_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
# 第三个数据源
spring.shardingsphere.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds2.jdbc-url=jdbc:mysql://172.16.185.176:3306/itechthink_order_2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=123456
# 指定workId
spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=1
# id生成策略
spring.shardingsphere.sharding.tables.t_order.key-generator.column=id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
# 指定 t_order 表的数据节点
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order_$->{0..1},ds1.t_order_$->{0..1},ds2.t_order_$->{0..1}
# Hint分片算法
# 理论上应该自定义两个不同的类分别用于分库和分表,但其实也可以共用一个类
spring.shardingsphere.sharding.tables.t_order.table-strategy.hint.algorithm-class-name=com.itechthink.strategy.CustomHintShardingAlgorithm
spring.shardingsphere.sharding.tables.t_order.database-strategy.hint.algorithm-class-name=com.itechthink.strategy.CustomHintShardingAlgorithm
先用inline
方式分库分表,使用SnowFlake算法生成订单id
。
最后在ShardingJDBCTest
中增加测试方法testHint()
。
@Test
public void testHint() {
// 获取对应的实例
HintManager hintManager = HintManager.getInstance();
/**
* 注意
*
* hintManager.addDatabaseShardingValue("t_order", 3L);用于设置库的分片键值,value是用于库分片取模
* hintManager.addTableShardingValue("t_order", 2L); 用于设置表的分片键值,value是用于表分片取模
*
* 1. 如果这两个值都不指定,那么ShardingSphere将执行全库路由
* 2. 如果仅指定addDatabaseShardingValue,那么ShardingSphere会查找第一个库中全部的`t_order`表
* 3. 如果仅指定addTableShardingValue,那么ShardingSphere会查找每个库中的`t_order_0`表
* 4. 如果这两个值都指定了,那么ShardingSphere则只会查找`ds0`库中的`t_order_0`表
*/
hintManager.addDatabaseShardingValue("t_order", 3L);
hintManager.addTableShardingValue("t_order", 2L);
// 强制读主库
// hintManager.setMasterRouteOnly();
// 对应的value只做查询
List<Order> list = orderMapper.selectList(new QueryWrapper<Order>().eq("id", 1823682382864113665L));
System.out.println(Arrays.toString(list.toArray()));
}
通过注释hintManager.addDatabaseShardingValue()
或hintManager.addTableShardingValue()
方法,执行4种不同的组合后,结果确实如注释中所指出的那样。
所以其实Hint分片策略
同样面临一个尴尬的问题:为了避免全库路由
而导致查询不到数据。
接着清除之前所有的测试数据。
> USE itechthink_order_0;
> TRUNCATE TABLE t_order_0;
> TRUNCATE TABLE t_order_1;
> USE itechthink_order_1;
> TRUNCATE TABLE t_order_0;
> TRUNCATE TABLE t_order_1;
> USE itechthink_order_2;
> TRUNCATE TABLE t_order_0;
> TRUNCATE TABLE t_order_1;
然后修改配置文件application.properties
,内容如下。
spring.application.name=mysql-sharding-jdbc
server.port=8080
# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true
# 数据源
spring.shardingsphere.datasource.names=ds0,ds1,ds2
# 第一个数据源
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://172.16.185.176:3306/itechthink_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&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://172.16.185.176:3306/itechthink_order_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
# 第三个数据源
spring.shardingsphere.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds2.jdbc-url=jdbc:mysql://172.16.185.176:3306/itechthink_order_2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=123456
# 指定workId
spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=1
# id生成策略
spring.shardingsphere.sharding.tables.t_order.key-generator.column=id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
# 指定 t_order 表的数据节点
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order_$->{0..1},ds1.t_order_$->{0..1},ds2.t_order_$->{0..1}
# Hint分片算法
# 指定分库键为userid
spring.shardingsphere.sharding.tables.t_order.database-strategy.hint.sharding-column=userid
spring.shardingsphere.sharding.tables.t_order.database-strategy.hint.algorithm-expression=ds$->{userid % 3}
# 指定分表键为id
spring.shardingsphere.sharding.tables.t_order.table-strategy.hint.sharding-column=id
spring.shardingsphere.sharding.tables.t_order.table-strategy.hint.algorithm-expression=t_order_$->{id % 2}
# 理论上应该自定义两个不同的类分别用于分库和分表,但其实也可以共用一个类
#spring.shardingsphere.sharding.tables.t_order.table-strategy.hint.algorithm-class-name=com.itechthink.strategy.CustomHintShardingAlgorithm
#spring.shardingsphere.sharding.tables.t_order.database-strategy.hint.algorithm-class-name=com.itechthink.strategy.CustomHintShardingAlgorithm
然后执行ShardingJDBCTest
中的testSaveOrder()
方法,在使用Hint分片策略
的情况下插入数据。
之后再还原application.properties
配置文件,让testSaveOrder()
方法通过Hint分片策略
插入数据。
两种不同插入数据的配置,最后执行结果完全一样:Hint分片策略
会在所有的库和所有的表中都插入同样的数据。
这又是Hint分片策略
另一个尴尬的问题:它既不能在配置文件中,也不能在代码中指定分片键
——其实也就等同于没有分片键
。
这也是为什么Hint分片策略
可以只查询单库单表而不必全库路由
的原因:因为它压根就没有分库分表。
感谢支持
更多内容,请移步《超级个体》。