一、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,转载请注明。