Sharding-JDBC动态水平分表实现
背景:
- 在项目中遇到了按照日期动态水平分表的需求,系统属于监控系统,每10分钟保存一次监控数据,并且每次要采集200个节点上的数据,即每次采集数据(间隔10分钟),向数据表添加200条记录,这样一个月数据表就有将近100万条记录。
- 为了控制单表数据量,并且为了方便后期数据统计,所以,每个月创建一张新表,之后的采集数据都写到新表中,例如报警信息表:alarm_histrtory表,按照日期水平分表alarm_histrtory201912,alarm_histrtory202001,alarm_histrtory202002,分别代表2019年12月、2020年1月、2020年2月的数据表。
- 每个月都需要创建一张新的数据表,但是Sharding-JDBC水平分表不能动态变化,所以,为了实现Sharding-JDBC的水平分表配置随着时间,动态修改,而无需程序重启。
- 示例代码:https://github.com/xujingle1995/sharding-jdbc
解决方案:
方案一:
通过配置中心,修改配置文件,然后sharding-jdbc自动获取新的分库分表配置,从而实现动态修改。这个方案还是需要人的介入,如果需要了解这种方案,只需要springboot中引入nacos配置中心即可。如果nacos不会配置可以参考:
1.官方文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
2.相关博客:https://blog.youkuaiyun.com/qq_26932225/article/details/86536837、https://blog.youkuaiyun.com/qq_26932225/article/details/86548829
方案二:
只需要以下三步:
1.自定义分片算法类
2.添加spring定时任务,动态修改Sharding-JDBC的配置。
3.配置application.properties配置文件
开始实践
1.准备工作:
- 创建Springboot工程,pom.xml引入mysql、mybatis、sharding-jdbc依赖
- 创建数据库:见本文最后的SQL语句
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- sharding-jdbc依赖 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
</dependencies>
2.自定义分片算法类(重要)
自定义分片算法类,用于当SQL语句中包含了分片键,sharding-jdbc会调用该类的doSharding方法,得到要查询的实际数据表名我这里自定义乐standard的精确分片和范围分片:
package com.xjl.sharding.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
/**
* @Title: dongfangdianqi
* @description: alarmHistory 精确分片 = in
* @create: 2020-02-25 14:12
* @update: 2020-02-25 14:12
* @updateRemark: 修改内容
* @Version: 1.0
*/
@Slf4j
public class PreciseSharingTableAlgorithmOfAlarmhis implements PreciseShardingAlgorithm<Date> {
private SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMM");
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
StringBuffer tableName = new StringBuffer();
log.info("执行操作的表名{}",shardingValue.getLogicTableName() + dateformat.format(shardingValue.getValue()));
tableName.append(shardingValue.getLogicTableName()).append(dateformat.format(shardingValue.getValue()));
return tableName.toString();
}
}
package com.xjl.sharding.config;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @Title: dongfangdianqi
* @description: 根据发生时间的范围查询分片算法 between and
* @create: 2020-02-25 16:55
* @update: 2020-02-25 16:55
* @updateRemark: 修改内容
* @Version: 1.0
*/
@Slf4j
public class RangeShardingAlgorithmOfAlarmhis implements RangeShardingAlgorithm<Date> {
private static SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMM");
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> shardingValue) {
Collection<String> result = new LinkedHashSet<>();
Range<Date> shardingKey = shardingValue.getValueRange();
// 获取起始,终止时间范围
Date startTime = shardingKey.lowerEndpoint();
Date endTime = shardingKey.upperEndpoint();
Date now = new Date();
if (startTime.after(now)){
startTime = now;
}
if (endTime.after(now)){
endTime = now;
}
Collection<String> tables = getRoutTable(shardingValue.getLogicTableName(), startTime, endTime);
if (tables != null && tables.size() >0</