在研究分表的时候,我他喵的是真的体会到了"浩如烟海"是啥意思.要么就是资料太老,要么就很少是按照月份分表的. 中间我走了很多弯路,甚至一度怀疑jap不适合用来进行分表.
从我看过的资料来说,没有一个博客介绍使用Shardingsphere进行分表是思路是什么样的,这也是我走了很多弯路的原因.
先介绍背景: 单个数据库,按照月份进行分表,每个表有月份的后缀,类似于: table_202006.
我先说下我的误区, 一开始我以为分表查询只需要配置库级别的分表规则就行了.这种想法是错误的,Shardingsphere是按照表级别进行配置的, 也就是说你想要分表查询哪个就配置对应表的分表规则. 其他没有配置分表规则的表则还是按照基本的表名进行查询.
还有一个坑, 一般我们会打印sql, 引入Shardingsphere之后,原本的打印的sql并不会是最后路由的表.举个栗子, 我们原本的查询"select * from base". 分表之后我们的查询最后路由到了base_202006表上面, 但是sql显示的还是"select * from base". 需要我们开启Shardingsphere的日志才能看到最终的路由.
具体的版本,根据时间自行更新
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>sharding-jdbc-spring-boot-starterartifactId>
<version>4.1.1version>
dependency>
看到这个名字,相信大家应该都知道了还有一个application.yml配置.分表的关键配置都在application-dev.yml, 所以直贴这个配置的内容了.
#配置数据源
spring:
#配置 Jpa
jpa:
hibernate:
# 生产环境设置成 none,避免程序运行时自动更新数据库结构
ddl-auto: none
shardingsphere:
datasource:
names: db0 # 数据源名称
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/database?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: root
password: password
sharding:
tables:
base: # 表名
actual-data-nodes: db0.base_$->{2019..2020}0$->{1..9}, db0.base_$->{2019..2020}$->{10..12}
table-strategy:
standard:
sharding-column: date # 分片键
precise-algorithm-class-name: com.bihu.kankan.config.TableShardingAlgorithm # 实现类的完全限定类名
range-algorithm-class-name: com.bihu.kankan.config.TableRangeShardingAlgorithm
props:
sql:
show: true
整个配置的重点都在shardingsphere
这个级别下面,我来一个个的解释:
=
的时候会触发此规则.PreciseShardingAlgorithm
中的泛型类型和上面指定的sharding-column
类型有关
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
/**
* 根据当前的月份进行分表查询
* @author LiYilin
*/
@Component
public class TableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
// 基本的表名_年份月份 base_199001
String targetTable = preciseShardingValue.getLogicTableName() + "_" + LocalDate.now().format(formatter);
if (availableTargetNames.contains(targetTable)){
return targetTable;
}
throw new UnsupportedOperationException("无效的表名称: " + targetTable);
}
}
TableRangeShardingAlgorithm.java
import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
@Component
public class TableRangeShardingAlgorithm implements RangeShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Integer> rangeShardingValue) {
Collection<String> collect = new ArrayList<>();
Range<Integer> valueRange = rangeShardingValue.getValueRange();
//TODO 这种写法只支持between, 但是效率很高
String between = rangeShardingValue.getLogicTableName() + "_" + String.valueOf(valueRange.lowerEndpoint()).substring(0, 6);
String and = rangeShardingValue.getLogicTableName() + "_" + String.valueOf(valueRange.upperEndpoint()).substring(0, 6);
for (String each : collection) {
if (between.equals(each) || and.equals(each)) {
collect.add(each);
}
}
return collect;
}
}
至此,一个分表操作基本完成.