sharding-jdbc 实现动态分表(按年按月)

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版本支持动态扩容了,大家有兴趣可以去研究下哈

你可能感兴趣的:(java,sharding,动态创建表)