一、Sharding-Jdbc实战之标准分片策略-精准分片算法《分表》
- StandardShardingStrategy
- 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
- PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
- RangeShardingAlgorithm 范围分片 是可选的,用于处理BETWEEN AND分片
- 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
CustomTablePreciseShardingAlgorithm
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.util.Collection;
public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param preciseShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> preciseShardingValue) {
for(String datasourceName : dataSourceNames){
String value = preciseShardingValue.getValue() % dataSourceNames.size() + "";
//product_order_0
if(datasourceName.endsWith(value)){
return datasourceName;
}
}
return null;
}
}
配置文件(注释其他)
点击查看完整内容
spring.application.name=xdclass-sharding-jdbc
server.port=8080
# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true
# 数据源 db0
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://8.142.19.202:3306/xdclass_shop_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=Mll6551012
# 第二个数据库
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://8.142.19.202:3306/xdclass_shop_order_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=Mll6551012
#配置workId
spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
#配置广播表
spring.shardingsphere.sharding.broadcast-tables=ad_config
spring.shardingsphere.sharding.tables.ad_config.key-generator.column=id
spring.shardingsphere.sharding.tables.ad_config.key-generator.type=SNOWFLAKE
#配置【默认分库策略】
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=ds$->{user_id % 2 }
#配置分库规则
#spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 2 }
#id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
# 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},
# 但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
#spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
#spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
# 指定product_order表的分片策略,分片策略包括【分片键和分片算法】
#spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=id
#spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{id % 2}
# 指定product_order_item表的分片策略,分片策略包括【分片键和分片算法】
#spring.shardingsphere.sharding.tables.product_order_item.actual-data-nodes=ds$->{0..1}.product_order_item_$->{0..1}
#spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.sharding-column=product_order_id
#spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.algorithm-expression=product_order_item_$->{product_order_id % 2}
#配置绑定表
#spring.shardingsphere.sharding.binding‐tables[0] = product_order,product_order_item
#精准分片-水平分表
# 指定product_order表的数据分布情况,配置数据节点,在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
#指定精准分片算法(水平分表)
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomTablePreciseShardingAlgorithm
关键位置
#精准分片-水平分表
# 指定product_order表的数据分布情况,配置数据节点,在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
#指定精准分片算法(水平分表)
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomTablePreciseShardingAlgorithm
随意测试一下
public void testSaveProductOrder(){
Random random = new Random();
for(int i=0; i<20;i++){
ProductOrderDO productOrderDO = new ProductOrderDO();
productOrderDO.setCreateTime(new Date());
productOrderDO.setNickname("大白菜i="+i);
productOrderDO.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrderDO.setPayAmount(100.00);
productOrderDO.setState("PAY");
productOrderDO.setUserId( Long.valueOf(random.nextInt(50)) );
productOrderMapper.insert(productOrderDO);
}
} 二、Sharding-Jdbc实战之标准分片策略-精准分片算法《分库分表》
配置文件
#精准分片-水平分表
# 指定product_order表的数据分布情况,配置数据节点,在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
#指定精准分片算法(水平分库) 根据user_id分库
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomDBPreciseShardingAlgorithm java代码
package net.xdclass.strategy;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.util.Collection;
public class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param preciseShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> preciseShardingValue) {
for(String datasourceName : dataSourceNames){
String value = preciseShardingValue.getValue() % dataSourceNames.size() + "";
//ds0、ds1
if(datasourceName.endsWith(value)){
return datasourceName;
}
}
return null;
}
}
三、Sharding-Jdbc分片策略实战之标准分片策略-范围分片算法
- RangeShardingAlgorithm 范围分片
- 用于处理BETWEEN AND语法,没配置的话会报错 Cannot find range sharding strategy in sharding rule.
- 主要是会根据 SQL中给出的分片健值范围值处理分库、分表逻辑
- 编码
package net.xdclass.strategy;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Long> shardingValue) {
Set<String> result = new LinkedHashSet<>();
//between 开始值
Long lower = shardingValue.getValueRange().lowerEndpoint();
//between 结束值
Long upper = shardingValue.getValueRange().upperEndpoint();
for(long i=lower;i<=upper;i++){
for(String datasource : dataSourceNames){
String value = i % dataSourceNames.size() +"";
if(datasource.endsWith(value)){
result.add(datasource);
}
}
}
return result;
}
}
配置文件
#范围分片(水平分表) spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=net.xdclass.strategy.CustomRangeShardingAlgorithm
简单测试
@Test
public void testBetween(){
productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().between("id",1L,2L));
}
四、Sharding-Jdbc分片策略实战之复合分片算法
- 复合分片算法ComplexShardingStrategy (了解即可)
- 提供对SQL语句中的=, IN和BETWEEN AND的分片操作,支持【多分片键】
- 由于多分片键之间的关系复杂,Sharding-JDBC并未做过多的封装
- 而是直接将分片键值组合以及分片操作符交于算法接口,全部由应用开发者实现,提供最大的灵活度
- 编码
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param complexKeysShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, ComplexKeysShardingValue<Long> complexKeysShardingValue) {
// 得到每个分片健对应的值
Collection<Long> orderIdValues = this.getShardingValue(complexKeysShardingValue, "id");
Collection<Long> userIdValues = this.getShardingValue(complexKeysShardingValue, "user_id");
List<String> shardingSuffix = new ArrayList<>();
// 对两个分片健取模的方式 product_order_0_0、product_order_0_1、product_order_1_0、product_order_1_1
for (Long userId : userIdValues) {
for (Long orderId : orderIdValues) {
String suffix = userId % 2 + "_" + orderId % 2;
for (String databaseName : dataSourceNames) {
if (databaseName.endsWith(suffix)) {
shardingSuffix.add(databaseName);
}
}
}
}
return shardingSuffix;
}
/**
* shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnNameAndShardingValuesMap 存储多个分片健 包括key-value
* key:分片key,id和user_id
* value:分片value,66和99
*
* @return shardingValues 集合
*/
private Collection<Long> getShardingValue(ComplexKeysShardingValue<Long> shardingValues, final String key) {
Collection<Long> valueSet = new ArrayList<>();
Map<String, Collection<Long>> columnNameAndShardingValuesMap = shardingValues.getColumnNameAndShardingValuesMap();
if (columnNameAndShardingValuesMap.containsKey(key)) {
valueSet.addAll(columnNameAndShardingValuesMap.get(key));
}
return valueSet;
}
}
配置文件
## 复合分片算法,order_id,user_id 同时作为分片健 spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.sharding-columns=user_id,id spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.algorithm-class-name=net.xdclass.strategy.CustomComplexKeysShardingAlgorithm
五、Sharding-Jdbc分片策略实战之Hint分片算法
- Hint分片策略HintShardingStrategy
- hint的中文意思:提示、暗示
- 这种分片策略无需配置文件进行配置分片健,分片健值也不再从 SQL中解析,外部手动指定分片健或分片库,让 SQL在指定的分库、分表中执行
- 通过Hint代码指定的方式而非SQL解析的方式分片的策略
- Hint策略会绕过SQL解析的,对于这些比较复杂的需要分片的查询,Hint分片策略性能可能会更好
- 可以指定sql去某个库某个表进行执行
- 编码(自定义完算法只实现了一部分,需要在调用 SQL 前通过
HintManager指定分库、分表信息)
分表
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 CustomTableHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param hitShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),hit策略此处为空 ""
*
* value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
* HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
* hintManager.addTableShardingValue("product_order", 1)参数进行指定
* @return
*/
@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 = shardingValue % dataSourceNames.size()+"";
if(datasourceName.endsWith(value)){
result.add(datasourceName);
}
}
}
return result;
}
} 分库
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 CustomDBHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param hitShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),hit策略此处为空 ""
*
* value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
* HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
* hintManager.addTableShardingValue("product_order", 1)参数进行指定
* @return
*/
@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 = shardingValue % dataSourceNames.size()+"";
if(datasourceName.endsWith(value)){
result.add(datasourceName);
}
}
}
return result;
}
} 配置文件
#hit分片 spring.shardingsphere.sharding.tables.product_order.database-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomDBHintShardingAlgorithm spring.shardingsphere.sharding.tables.product_order.table-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomTableHintShardingAlgorithm
测试
/**
* 正常可以用AOP进行实现
*/
@Test
public void testHit(){
//清除历史规则
HintManager.clear();
//获取对应的实例
HintManager hintManager = HintManager.getInstance();
//设置库的分片键值,value是用于库分片取模
hintManager.addDatabaseShardingValue("product_order",3L);
//设置表的分片键值,value是用于表分片取模
hintManager.addTableShardingValue("product_order",8L);
//如果在读写分离数据库中,Hint 可以强制读主库(主从复制存在一定延时,但在业务场景中,可能更需要保证数据的实时性)
//hintManager.setMasterRouteOnly();
//对应的value,只做查询,不做sql解析
productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id",66L));
}
本文作者为DBC,转载请注明。


