分库分表概念
- 垂直角度(表结构不一样,大结构分多个小结构)
- 垂直分表: 将一个表字段拆分多个表,每个表存储部分字段
- 好处: 避免IO时锁表的次数,分离热点字段和非热点字段,避免大字段IO导致性能下降
- 原则:业务经常组合查询的字段一个表;不常用字段一个表;text、blob类型字段作为附属表
- 垂直分库:根据业务将表分类,放到不同的数据库服务器上
- 好处:避免表之间竞争同个物理机的资源,比如CPU/内存/硬盘/网络IO
- 原则:根据业务相关性进行划分,领域模型,微服务划分一般就是垂直分库
- 垂直分表: 将一个表字段拆分多个表,每个表存储部分字段
- 水平角度(表结构一样,内容分散)
- 水平分库:把同个表的数据按照一定规则分到不同的数据库中,数据库在不同的服务器上
- 好处: 多个数据库,降低了系统的IO和CPU压力
- 原则
- 选择合适的分片键和分片策略,和业务场景配合
- 避免数据热点和访问不均衡、避免二次扩容难度大
- 水平分表:同个数据库内,把一个表的数据按照一定规则拆分到多个表中,对数据进行拆分,不影响表结构
- 单个表的数据量少了,业务SQL执行效率高,降低了系统的IO和CPU压力
- 原则
- 选择合适的分片键和分片策略,和业务场景配合
- 避免数据热点和访问不均衡、避免二次扩容难度大
- 水平分库:把同个表的数据按照一定规则分到不同的数据库中,数据库在不同的服务器上
水平分库分表常见策略
Range
方案一:自增id,根据ID范围进行分表(左闭右开)
- 规则案例
- 1~1,000,000 是 table_1
- 1,000,000 ~2,000,000 是 table_2
- 2,000,000~3,000,000 是 table_3
- ...更多
- 优点
- id是自增长,可以无限增长
- 扩容不用迁移数据,容易理解和维护
- 缺点
- 大部分读和写都访会问新的数据,有IO瓶颈,整体资源利用率低
- 数据倾斜严重,热点数据过于集中,部分节点有瓶颈
Hash
方案二:hash取模(Hash分库分表是最普遍的方案)
-
案例规则
- 用户ID是整数型的,要分2库,每个库表数量4表,一共8张表
- 用户ID取模后,值是0到7的要平均分配到每张表
A库ID = userId % 库数量 2
表ID = userId / 库数量 2 % 表数量4
- 例子
userId id % 2 (库-取余) id /2 % 4 (表) 1 1 0 2 0 1 3 1 1 4 0 2 5 1 2 6 0 3 7 1 3 8 0 0 9 1 0 -
优点
- 保证数据较均匀的分散落在不同的库、表中,可以有效的避免热点数据集中问题,
-
缺点
- 扩容不是很方便,需要数据迁移
SharingSphere
ShardingJdbc术语
-
数据节点Node
- 数据分片的最小单元,由数据源名称和数据表组成
- 比如:ds_0.product_order_0
-
真实表
- 在分片的数据库中真实存在的物理表
- 比如订单表 product_order_0、product_order_1、product_order_2
-
逻辑表
- 水平拆分的数据库(表)的相同逻辑和数据结构表的总称
- 比如订单表 product_order_0、product_order_1、product_order_2,逻辑表就是product_order
-
绑定表
- 指分片规则一致的主表和子表
- 比如product_order表和product_order_item表,均按照order_id分片,则此两张表互为绑定表关系
- 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升
-
广播表
- 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
- 适用于数据量不大且需要与海量数据的表进行关联查询的场景
- 例如:字典表、配置表
ShardingJdbc算法
-
数据库表分片(水平库、表)
- 包含分片键和分片策略
-
分片键 (PartitionKey)
- 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段
- 比如prouduct_order订单表,根据订单号 out_trade_no做哈希取模,则out_trade_no是分片键
- 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片
-
分片策略
-
行表达式分片策略 InlineShardingStrategy(必备)
-
只支持【单分片键】使用Groovy的表达式,提供对SQL语句中的 =和IN 的分片操作支持
-
可以通过简单的配置使用,无需自定义分片算法,从而避免繁琐的Java代码开发
-
prouduct_order_$->{user_id % 8}` 表示订单表根据user_id模8,而分成8张表,表名称为`prouduct_order_0`到`prouduct_order_7
-
-
标准分片策略StandardShardingStrategy(需了解)
- 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
- PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
- RangeShardingAlgorithm 范围分配 是可选的,用于处理BETWEEN AND分片
- 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
-
复合分片策略ComplexShardingStrategy(需了解)
- 支持【多分片键】,多分片键之间的关系复杂,由开发者自己实现,提供最大的灵活度
- 提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持
-
Hint分片策略HintShardingStrategy(需了解)
- 这种分片策略无需配置分片健,分片健值也不再从 SQL中解析,外部手动指定分片健或分片库,让 SQL在指定的分库、分表中执行
- 用于处理使用Hint行分片的场景,通过Hint而非SQL解析的方式分片的策略
- Hint策略会绕过SQL解析的,对于这些比较复杂的需要分片的查询,Hint分片策略性能可能会更好
-
不分片策略 NoneShardingStrategy(需了解)
- 不分片的策略。
-
SpringBoot整合
框架版本说明
<properties>
<!--JDK版本,如果是jdk8则这里是 1.8-->
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.boot.version>2.5.5</spring.boot.version>
<mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
<lombok.version>1.18.16</lombok.version>
<sharding-jdbc.version>4.1.1</sharding-jdbc.version>
<junit.version>4.12</junit.version>
<druid.version>1.1.16</druid.version>
<!--跳过单元测试-->
<skipTests>true</skipTests>
</properties>
maven pom文件配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-test</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
<!--mybatis plus和springboot整合-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${sharding-jdbc.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
多数据库配置
spring.application.name=xdclass-sharding-jdbc
server.port=8080
logging.level.root=INFO
# 打印执行的数据库以及语句
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://ip:33306/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=root
# 第二个数据库
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://ip:33306/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=root
水平分表配置
# 指定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}
public class ProductOrderDO {
@TableId(value = "id",type = IdType.AUTO)
private Long id;
private String outTradeNo;
private String state;
private Date createTime;
private Double payAmount;
private String nickname;
private Long userId;
}
- 问题
- 主键重复问题(两张表都会生成数据,ID自增会产生同样的ID)
分布式ID生成器
## workerId配置
spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
## id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
注意:
- 注释掉实体类中ID增长注解
- workerId需要配置Id生成策略,不然会报错
广播表
- 什么是广播表
- 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
- 适用于数据量不大且需要与海量数据的表进行关联查询的场景
- 例如:字典表、配置表
- 注意点:
- 分库分表中间件,对应的数据库字段,不能是sql的关键字,否则容易出问题,且报错不明显
- 配置实战
#配置广播表
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
水平分库分表配置
# 多数据源 db0
spring.shardingsphere.datasource.names=ds0,ds1
#配置分库规则
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}
# 分表策略修改为多数据库
#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表和product_order_item表,均按照order_id分片,则此两张表互为绑定表关系
- 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升
- 表讲解
CREATE TABLE `product_order_item_0` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_order_id` bigint DEFAULT NULL COMMENT '订单号',
`product_id` bigint DEFAULT NULL COMMENT '产品id',
`product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
`buy_num` int DEFAULT NULL COMMENT '购买数量',
`user_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
- 配置实战
#分库策略 默认
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression = ds$->{user_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
- 未使用绑定表的SQL
- 使用绑定表后的SQL
多种分片策略
精准分片
-
StandardShardingStrategy
- 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
- PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
- RangeShardingAlgorithm 范围分片 是可选的,用于处理BETWEEN AND分片
- 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
-
代码
-
public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> { /** * * @param dataSourceNames 数据源集合 * 在分库时值为所有分片库的集合 databaseNames * 分表时为对应分片库中所有分片表的集合 tablesNames * * @param shardingValue 分片属性,包括 * logicTableName 为逻辑表, * columnName 分片健(字段), * value 为从 SQL 中解析出的分片健的值 * @return */ @Override public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) { for (String databaseName : dataSourceNames) { String value = shardingValue.getValue() % dataSourceNames.size() + ""; //value是0,则进入0库表,1则进入1库表 if (databaseName.endsWith(value)) { return databaseName; } } throw new IllegalArgumentException(); } }
-
新增订单记录
Random random = new Random();
for(int i=0;i<5;i++){
ProductOrder productOrder = new ProductOrder();
productOrder.setCreateTime(new Date());
productOrder.setNickname("000二当家i="+i);
productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrder.setPayAmount(100.00);
productOrder.setState("PAY");
int value = random.nextInt(100);
productOrder.setUserId(Long.valueOf(value));
productOrderMapper.insert(productOrder);
}
- 配置文件(注释其他)
# 指定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 class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) {
for (String databaseName : dataSourceNames) {
String value = shardingValue.getValue() % dataSourceNames.size() + "";
//value是0,则进入0库表,1则进入1库表
if (databaseName.endsWith(value)) {
return databaseName;
}
}
throw new IllegalArgumentException();
}
}
- 新增订单记录
Random random = new Random();
for(int i=0;i<5;i++){
ProductOrder productOrder = new ProductOrder();
productOrder.setCreateTime(new Date());
productOrder.setNickname("000二当家i="+i);
productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrder.setPayAmount(100.00);
productOrder.setState("PAY");
int value = random.nextInt(100);
productOrder.setUserId(Long.valueOf(value));
productOrderMapper.insert(productOrder);
}
- 分库(自定义分库和分表算法的实现基本是一样的)
#指定全部数据节点,水平分库分表
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
# 分库分片健
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
范围分片
- RangeShardingAlgorithm 范围分片
- 用于处理BETWEEN AND语法,没配置的话会报错 Cannot find range sharding strategy in sharding rule.
- 主要是会根据 SQL中给出的分片健值范围值处理分库、分表逻辑
- 代码
public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Long> rangeShardingValue) {
Set<String> result = new LinkedHashSet<>();
// between 起始值
Long lower = rangeShardingValue.getValueRange().lowerEndpoint();
// between 结束值
Long upper = rangeShardingValue.getValueRange().upperEndpoint();
// 循环范围计算分库逻辑
for (long i = lower; i <= upper; i++) {
for (String databaseName : dataSourceNames) {
if (databaseName.endsWith(i % dataSourceNames.size() + "")) {
result.add(databaseName);
}
}
}
return result;
}
}
- 配置
#精准水平分表下,增加一个范围分片
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=net.xdclass.strategy.CustomRangeShardingAlgorithm
- 测试代码
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().between("id",1L,1L));
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().between("id",1L,3L));
复合分片
- 复合分片算法ComplexShardingStrategy (了解即可)
- 提供对SQL语句中的=, IN和BETWEEN AND的分片操作,支持【多分片键】
- 由于多分片键之间的关系复杂,Sharding-JDBC并未做过多的封装
- 而是直接将分片键值组合以及分片操作符交于算法接口,全部由应用开发者实现,提供最大的灵活度
- 编码
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
/**
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames:比如表:product_order_0/product_order_1、库ds0/ds1 等
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnNameAndShardingValuesMap 存储多个分片健,包括key-value
* @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<>();
// 对两个分片健取模的方式
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;
}
}
- 配置(分表)
- 记得注释其他策略,否则报错 Only allowed 0 or 1 sharding strategy configuration
## 复合分片算法,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
- 测试代码
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().eq("id",66L).eq("user_id",99L));
Hint分片
-
Hint分片策略HintShardingStrategy
- hint的中文意思:提示、暗示
- 这种分片策略无需配置文件进行配置分片健,分片健值也不再从 SQL中解析,外部手动指定分片健或分片库,让 SQL在指定的分库、分表中执行
- 通过Hint代码指定的方式而非SQL解析的方式分片的策略
- Hint策略会绕过SQL解析的,对于这些比较复杂的需要分片的查询,Hint分片策略性能可能会更好
- 可以指定sql去某个库某个表进行执行
-
编码(自定义完算法只实现了一部分,需要在调用 SQL 前通过
HintManager
指定分库、分表信息)-
分库
-
public class CustomDBHintShardingAlgorithm implements HintShardingAlgorithm<Long> { /** * * @param dataSourceNames 数据源集合 * 在分库时值为所有分片库的集合 databaseNames * 分表时为对应分片库中所有分片表的集合 tablesNames * * @param hintShardingValue 分片属性,包括 * logicTableName 为逻辑表, * columnName 分片健(字段),hit策略此处为空 "" * * value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断 * HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过 * hintManager.addTableShardingValue("product_order", 1)参数进行指定 * @return */ @Override public Collection<String> doSharding(Collection<String> dataSourceNames, HintShardingValue<Long> hintShardingValue) { Collection<String> result = new ArrayList<>(); for (String tableName : dataSourceNames) { for (Long shardingValue : hintShardingValue.getValues()) { if (tableName.endsWith(String.valueOf(shardingValue % dataSourceNames.size()))) { result.add(tableName); } } } return result; } }
-
分表
-
public class CustomTableHintShardingAlgorithm implements HintShardingAlgorithm<Long> { /** * * @param dataSourceNames 数据源集合 * 在分库时值为所有分片库的集合 databaseNames * 分表时为对应分片库中所有分片表的集合 tablesNames * * @param hintShardingValue 分片属性,包括 * logicTableName 为逻辑表, * columnName 分片健(字段),hit策略此处为空 "" * * value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断 * HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过 * hintManager.addTableShardingValue("product_order", 1)参数进行指定 * @return */ @Override public Collection<String> doSharding(Collection<String> dataSourceNames, HintShardingValue<Long> hintShardingValue) { Collection<String> result = new ArrayList<>(); for (String tableName : dataSourceNames) { for (Long shardingValue : hintShardingValue.getValues()) { if (tableName.endsWith(String.valueOf(shardingValue % dataSourceNames.size()))) { result.add(tableName); } } } return result; } }
-
-
配置(配置多个数据源ds)
# Hint分片算法
spring.shardingsphere.sharding.tables.product_order.table-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomTableHintShardingAlgorithm
spring.shardingsphere.sharding.tables.product_order.database-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomDBHintShardingAlgorithm
- 编码测试
@Test
public void testHint() {
// 清除掉历史的规则
HintManager.clear();
//Hint分片策略必须要使用 HintManager工具类
HintManager hintManager = HintManager.getInstance();
// 设置库的分片健,value用于库分片取模,
hintManager.addDatabaseShardingValue("product_order",3L);
// 设置表的分片健,value用于表分片取模,
//hintManager.addTableShardingValue("product_order", 7L);
hintManager.addTableShardingValue("product_order", 8L);
// 如果在读写分离数据库中,Hint 可以强制读主库(主从复制存在一定延时,但在业务场景中,可能更需要保证数据的实时性)
//hintManager.setMasterRouteOnly();
//对应的value只做查询,不做sql解析
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().eq("id", 66L));
}
- 之前没用partitionKey会触发全库表路由,发出很多不相干的SQL. 使用Hint方式是可以避免这个问题