ShardingSphere5.2.0按月分表,自动建表

1、依赖

<dependency>
   <groupId>org.apache.shardingsphere</groupId>
   <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
   <version>5.2.0</version>
</dependency>

2、配置文件

# 端口配置
server:
  port: 9020
  servlet:
    #    # 项目的前缀名
    context-path: /
# 开发环境配置
spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 500MB
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    #    password: root
    #swell*2021
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/swell?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2b8
    #url: jdbc:mysql://localhost:3306/swell?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2b8
    druid:
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 5
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
  shardingsphere:
    datasource:
      names: db0
      db0:
        data-source-name: com.alibaba.druid.pool.DruidDataSource
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/swell?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: root
    rules:
      sharding:
        # 分片算法配置
        shardingAlgorithms:
          # 分片算法名称,不支持大写字母和下划线,否则启动就会报错
          time-sharding-algorithm:
            props:
              # 分片策略
              strategy: standard
              # 自定义分配算法
              algorithmClassName: com.rookie.shardingsphere.CustomTimeShardingAlgorithm
            # 类型:自定义策略
            type: CLASS_BASED
        tables:
          mq_message:#要分表的表名
            logicTable: mq_message
            #自定义节点表
            actualDataNodes: db0.$->{com.rookie.shardingsphere.ShardingAlgorithmTool.cacheTableNames()}
            tableStrategy:
              # 使用标准分片策略
              standard:
                # 配置分片字段
                shardingColumn: create_time
                # 分片算法名称,不支持大写字母和下划线,否则启动就会报错
                shardingAlgorithmName: time-sharding-algorithm

    props:
      sql-show: true
  redis:
    host: 127.0.0.1 # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password:  # Redis服务器连接密码(默认为空)
    jedis:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        min-idle: 0 # 连接池中的最小空闲连接
    timeout: 3000ms # 连接超时时间(毫秒

mybatis-plus:
  mapper-locations: classpath*:/com/rookie/dao/mapper/*.xml
  configuration:
    #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    cache-enabled: true
#业务数据配置
biz:
  company:
    #默认统计供热开始时间,换热站累积能耗统计用
    default-hot-startDate: 10-31
    #默认统计供热结束时间,换热站累积能耗统计用
    default-hot-endDate: 05-01
  site:
    #热站未收到MQ消息时长限制,单位:分钟,超过则认为离线,同时产生告警
    mqdata-alert-minutes: 5

3、自定义分片算法CustomTimeShardingAlgorithm,根据自己实际情况可以修改。

import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author: 晨光1
 * @description: 自定义时间分片算法
 * @date 2024/5/31 15:33
 * @Version 1.0
 */
@Slf4j
public class CustomTimeShardingAlgorithm implements StandardShardingAlgorithm<Date> {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");


    /**
     * 精准分片
     * @param collection 对应分片库中所有分片表的集合
     * @param preciseShardingValue
     * @return 表名
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
        log.info("进入doSharding返回字符串");
        Object value = preciseShardingValue.getValue();
        String tableSuffix = null;
        if(value instanceof Date){
            LocalDate localDate = ((Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            tableSuffix = localDate.format(DateTimeFormatter.ofPattern("yyyyMM"));
        }else{
            String column = (String)value;
            tableSuffix = LocalDateTime.parse(column, formatter).format(DateTimeFormatter.ofPattern("yyyyMM"));
        }
        String logicTableName = preciseShardingValue.getLogicTableName();
        String actualTableName = logicTableName.concat("_").concat(tableSuffix);
        if(!collection.contains(actualTableName)){
            collection.add(actualTableName);
        }
        return actualTableName;
    }

    /**
     * 范围分片
     * @param collection 对应分片库中所有分片表的集合
     * @param rangeShardingValue 分片范围
     * @return 表名集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {
        log.info("进入doSharding返回列表");
        // 逻辑表名
        String logicTableName = rangeShardingValue.getLogicTableName();
        // 范围参数
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        //起始时间  结束时间
        LocalDateTime start = null;
        LocalDateTime end = null;
        Object lowerEndpoint = (Object)valueRange.lowerEndpoint();
        Object upperEndpoint = (Object)valueRange.upperEndpoint();
        log.info("lowerEndpoint ======"+lowerEndpoint.toString());
        log.info("upperEndpoint ======"+upperEndpoint.toString());
        if(lowerEndpoint instanceof  String){
            String lower = (String) lowerEndpoint;
            String upper = (String) upperEndpoint;
            start = LocalDateTime.parse(lower,formatter);
            end = LocalDateTime.parse(upper,formatter);
        }else{
            start = valueRange.lowerEndpoint().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            end = valueRange.upperEndpoint().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            log.info("start ======",start.format(DateTimeFormatter.ofPattern("yyyyMM")));
            log.info("end ======"+end.format(DateTimeFormatter.ofPattern("yyyyMM")));
        }
        if(end.isAfter(LocalDateTime.now())){
            end = LocalDateTime.now();
        }
        // 查询范围的表
        Set<String> queryRangeTables = extracted(logicTableName, start, end);
        return queryRangeTables;
    }

    @Override
    public String getType() {
        return null;
    }

    @Override
    public Properties getProps() {
        return null;
    }


    /**
     * 根据范围计算表名
     * @param logicTableName 表名前缀
     * @param lowerEndpoint 起点
     * @param upperEndpoint 终点
     * @return 最终的表名集合
     */
    private Set<String> extracted(String logicTableName, LocalDateTime lowerEndpoint, LocalDateTime upperEndpoint) {
        Set<String> rangeTable = new HashSet<>();
        while (lowerEndpoint.isBefore(upperEndpoint)) {
            String str = getTableNameByDate(lowerEndpoint, logicTableName);
            rangeTable.add(str);
            lowerEndpoint = lowerEndpoint.plusMonths(1);
        }
        // 获取物理表名
        String tableName = getTableNameByDate(upperEndpoint, logicTableName);
        rangeTable.add(tableName);
        return rangeTable;
    }
    /**
     * 根据日期获取表名
     * @param dateTime 日期
     * @param logicTableName 逻辑表名
     * @return 物理表名
     */
    private String getTableNameByDate(LocalDateTime dateTime, String logicTableName) {
        String tableSuffix = dateTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
        return logicTableName.concat("_").concat(tableSuffix);
    }

    @Override
    public void init(Properties properties) {

    }
}

4、分片算法工具类

import com.alibaba.fastjson.JSONObject;
import com.rookie.common.util.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource;

import javax.annotation.PostConstruct;
import java.sql.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.core.env.Environment;

@Slf4j
public class ShardingAlgorithmTool {

    private static final Set<String> tableNameCache = new CopyOnWriteArraySet<>();

    // 启动时,实际表中要有值,启动后,在ShardingTablesLoadRunner中先清空在缓存
    static  {
        //默认是当前月份的,最少有一个否则报错。
        String tableSuffix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String actualTableName = "mq_message".concat("_").concat(tableSuffix);
        tableNameCache.add(actualTableName);
    }

    @PostConstruct
    public void init() {

    }

    /**
     * 缓存重载方法
     */
    public static void tableNameCacheReload() {
        //读取数据库中所有表名
        List<String> tableList = getAllTableName();
        //清除缓存
        ShardingAlgorithmTool.tableNameCache.clear();
        //更新缓存
        ShardingAlgorithmTool.tableNameCache.addAll(tableList);
    }

    /**
     * 获取数据库中的表名
     */
    public static List<String> getAllTableName() {
        List<String> res = new ArrayList<>();
        // 获取数据中的表名
        Environment env = SpringUtil.getApplicationContext().getEnvironment();
        try (Connection connection = DriverManager.getConnection(env.getProperty("spring.datasource.url"), env.getProperty("spring.datasource.username"), env.getProperty("spring.datasource.password"));
             Statement st = connection.createStatement()) {
            try (ResultSet rs = st.executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA='swell' AND TABLE_NAME like 'mq_message_2%'")) {
                while (rs.next()) {
                    res.add(rs.getString(1));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 获取缓存中的表名
     * @return
     */
    public static Set<String> cacheTableNames() {
        return ShardingAlgorithmTool.tableNameCache;
    }
}

5、启动时设置自动建表任务

import com.rookie.dao.MqMessageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;

@Order(value = 1) // 数字越小,越先执行
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {

    @Autowired
    private MqMessageMapper mqMessageMapper;

    @Override
    public void run(String... args) {
        ShardingAlgorithmTool.tableNameCacheReload();
    }

    @Scheduled(cron = "0 0 23 * * ?")
    public void Scheduled(){
        final Calendar c = Calendar.getInstance();
        //c.get(Calendar.DATE) 当前时间
        //c.getActualMaximum(Calendar.DATE) 本月最后一日
        if (c.get(Calendar.DATE) == c.getActualMaximum(Calendar.DATE)) {
            c.add(Calendar.DATE, 1);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
            String monthStr = dateFormat.format(c.getTime());
            mqMessageMapper.createTable("mq_message_" + monthStr);
        }
    }
}

6、测试示例

下面的代码是查询条件

QueryWrapper<MqMessage> queryWrapper = new QueryWrapper<>();
queryWrapper.le("create_time",DatetimeUtils.stringToDate("2024-07-04 12:12:12","yyyy-MM-dd HH:mm:ss"));
queryWrapper.ge("create_time",DatetimeUtils.stringToDate("2024-04-04 12:12:12","yyyy-MM-dd HH:mm:ss"));
List<MqMessage> list = mqMessageService.list(queryWrapper);
return ApiResult.success(JSONObject.toJSONString(list));

结果

 

7、现在的缺点:如果查询时不指定时间段,默认是查询当前月份对应的表。

继续研究,争取能找出不设置时间段时默认全部表联合查询。有知道的给指点一下。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

StruggleRookie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值