1、 项目中我们希望 能够按照时间、类别来添加表。但是sharding-jdbc 是固定配置 的 actual-data-nodes 参数。
也就是说我们需要提前创建好分表或者分库。那么我们需要如何来实现动态创建表,并且动态刷新 actual-data-nodes 呢。
2、思路 就是 写个定时器来动态创建表 ,在创建表的时候 动态刷新 actual-data-nodes 实现动态创建表被shard加载。注意的是,我的任务 采用xxl-job (主要是我们的项目是分布式项目)你们如果是单体也可以直接使用架构自带的定时任务器。
3、不多废话直接上代码。(备注: 这里实现的方式 动态刷新 只能刷新单机,如果是集群部署,需要自己优化,具体的思路是 就是消息中间件的 topic 模式 订阅消费 调用 actualTablesRefresh方法刷新所有部署实例 )
maven 依赖
sharding-jdbc 依赖
org.apache.shardingsphere
sharding-jdbc-spring-boot-starter
4.0.0-RC1
org.apache.shardingsphere
sharding-jdbc-spring-namespace
4.0.0-RC1
任务管理器 XXl-job
com.xuxueli
xxl-job-core
2.3.0-SNAPSHOT
yml 配置:
spring:
### 处理连接池冲突 #####
main:
allow-bean-definition-overriding: true
### 这里采用的按年分表策略 采用一个库 扩展表的方式 多库同理 #####
shardingsphere:
props:
sql:
show: true
datasource:
names: ds0
### 主数据库 ###
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:${DATASOURCE_DBTYPE:mysql}://${DATASOURCE_HOST:127.0.0.1}:${DATASOURCE_PORT:3306}/sys_logs?characterEncoding=UTF-8&useUnicode=true&useSSL=false
username: ${DATASOURCE_USERNAME:root}
password: ${DATASOURCE_PASSWORD:123456}
sharding:
tables:
wc_passenger:
### 数据表 按年分表 #### 流量
actual-data-nodes: ds0.sys_log
table-strategy:
standard:
sharding-column: created_time
precise-algorithm-class-name: com.xxx.DeviceShardingAlgorithmConfig
range-algorithm-class-name: com.xxx.DeviceShardingAlgorithmConfig
### 动态创建表的表名称 创建时间开始年 #########
db:
table:
names: sys_log
startYear: 2021
4、 按年建表(按月同理) 入库分表策略
定时器任务是每年最后一天创建下一年的表。(可根据自己的需求自定义)
入库分表策略
public class LogShardingConfig implements PreciseShardingAlgorithm, RangeShardingAlgorithm {
@Override
public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
String target = shardingValue.getValue().toString();
return shardingValue.getLogicTableName() + "_" + target.substring(0,4);
}
@Override
public Collection doSharding(Collection availableTargetNames, RangeShardingValue shardingValue) {
Collection availables = new ArrayList<>();
Range valueRange = shardingValue.getValueRange();
for (String target : availableTargetNames) {
Integer shardValue = Integer.parseInt(target.substring(0,4));
if (valueRange.hasLowerBound()) {
String lowerStr = valueRange.lowerEndpoint().toString();
Integer start = Integer.parseInt(lowerStr.substring(0, 4));
if (start - shardValue > 0) {
continue;
}
}
if (valueRange.hasUpperBound()) {
String upperStr = valueRange.upperEndpoint().toString();
Integer end = Integer.parseInt(upperStr.substring(0, 4));
if (end - shardValue < 0) {
continue;
}
}
availables.add(target);
}
return availables;
}
}
5、 初始化 其实时间配置
@Component
@ConfigurationProperties(prefix = "db.table")
@Data
public class TableNamesConfig {
String[] names;
// 起始年 默认 2021
int startYear;
}
6、动态创建加载表
/**
* @author Administrator
* @title: lj
* @projectName mybatis-plus-sample-generator
* @description: TODO
* @date 2021/1/13 001316:58
*/
@Log4j2
@Component
@EnableConfigurationProperties(TableNamesConfig.class)
public class LogHandler {
@Autowired
private TableNamesConfig tableNamesConfig;
@Autowired
private DataSource dataSource;
@Autowired
private SysLogMapper sysLogMapper;
/**
* 开启拉取任务(Bean模式) 获取狗信息 以 年为单位创建 硬件上报数据表
*/
@XxlJob("createWcTableJobHandler")
public void CreateWcTableJobHandler() throws Exception {
XxlJobHelper.log("XXL-JOB, CreateWcTableJobHandler.");
Date date =new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy");
String s = format.format(date);
Integer data = Integer.parseInt(s) +1;
String name;
name = "sys_log_" + data;
boolean flag = true;
// 判断表是否存在 不存在则添加表
try{
sysLogMapper.selectTable(name);
flag =false;
}catch (Exception e){
flag = true;
}
try {
if (flag) {
// 这里创建表 建立采用 CREATE TABLE xx like sys_log 减少维护量
SysLogMapper.createTableCard(name);
}
}catch (Exception e){
e.printStackTrace();
}
// 这里特别重要 创建成功之后 刷新 actual-data-nodes
actualTablesRefresh(data);
}
/***
* 启动的时候初始化一下 动态表单的配置 初化当前年
*/
@PostConstruct
private void intData(){
Date date =new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy");
String s = format.format(date);
actualTablesRefresh(Integer.parseInt(s));
}
/**
* 动态更新 处理化的表配置
* @param data 表名称
*/
public void actualTablesRefresh(Integer data) {
try {
System.out.println("---------------------------------");
ShardingDataSource dataSource = (ShardingDataSource) this.dataSource;
if (tableNamesConfig.getNames() == null || tableNamesConfig.getNames().length == 0) {
log.info("dynamic.table.names为空");
return;
}
for (int i = 0; i < tableNamesConfig.getNames().length; i++) {
TableRule tableRule = null;
try {
tableRule = dataSource.getShardingContext().getShardingRule().getTableRule(tableNamesConfig.getNames()[i]);
} catch (ShardingConfigurationException e) {
log.error("报错啦" + e.getMessage());
}
List dataNodes = tableRule.getActualDataNodes();
Field actualDataNodesField = TableRule.class.getDeclaredField("actualDataNodes");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(actualDataNodesField, actualDataNodesField.getModifiers() & ~Modifier.FINAL);
actualDataNodesField.setAccessible(true);
List newDataNodes = new ArrayList<>();
int time = tableNamesConfig.getStartYear();
String dataSourceName = dataNodes.get(0).getDataSourceName();
while (true) {
DataNode dataNode = new DataNode(dataSourceName + "." + tableNamesConfig.getNames()[i]+ "_" + time);
newDataNodes.add(dataNode);
time = time + 1;
if (time > data.intValue()) {
break;
}
}
actualDataNodesField.set(tableRule, newDataNodes);
}
}catch (Exception e){
e.printStackTrace();
log.info("初始化 动态表单失败" + e.getMessage());
}
}
}
5版本支持动态扩容了,大家有兴趣可以去研究下哈