Springboot集成Shardingsphere分表分库-两步快速集成-极简方便亲测可用

前言

  因项目热门业务数据爆增,Mysql单表扛不住持续暴增数据量,集成使用Shardingsphere进行水平分表分库。这里记录下。

项目版本

springboot 2.7.5

shardingsphere-jdbc-core-spring-boot-starter 5.2.0

jdk 17

步骤一

pom.xml添加依赖

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

步骤二 

yml配置

spring:
  shardingsphere:
    mode:
      type: Standalone # Standalone=单机模式或 Cluster、Memory
    datasource:
      names: ds
      ds:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
        username: root
        password: root
    rules:
      sharding:
        tables:
          dp_im_users_info:
            # 定义实际的数据节点范围。这里表示 `dp_im_users_info` 表的数据会根据年份(2025 到 2035)和月份(1 -12 月)分布在不同的节点上
            actual-data-nodes: ds.dp_im_users_info_$->{202501..203512}
            table-strategy:
              standard:
                # 指定分片列为 `create_time`,即根据 `create_time` 列的值进行数据分片
                sharding-column: create_time
                # 指定分片算法的名称,这里使用之前定义的 `tba-monthly`,表示按月进行分片
                sharding-algorithm-name: tba-monthly
        sharding-algorithms:
# 自定义分库分表的算法 这里实现的是按年月份-水平分表不分库
#          tba-monthly:
#            type: CLASS_BASED
#            props:
#              strategy: STANDARD
#              algorithmClassName: com.shardingsphere.CustomTableShardingAlgorithm

#直接配置按年月份-水平分表不分库
          tba-monthly: #sharding-algorithm-name配置的名称
            type: INTERVAL  # 指定分片策略的类型为基于时间区间的分片策略
            props:
              datetime-pattern: yyyy-MM-dd HH:mm:ss  # 定义时间格式,表示日期和时间的格式为年-月-日 时:分:秒
              datetime-lower: 2025-01-01 00:00:00  # 设置分片的开始时间,即时间范围的下限
              datetime-upper: 2035-12-31 23:59:59  # 设置分片的结束时间,即时间范围的上限
              sharding-suffix-pattern: yyyyMM  # 定义生成分片表后缀的时间格式,这里是按年和月进行分片(如 202301, 202302 等)
              datetime-interval-amount: 1  # 定义分片的时间间隔大小,这里是每个间隔为 1(即每个月创建一个分片)
              datetime-interval-unit: MONTHS  # 指定时间间隔的单位,这里为按月分片
    props:
      sql-show: true # 开启 SQL 打印,便于调试
 

扩展说明:

第一点:Shardingsphere是不会自动创建水平分表的需要自己手动创建\预创建

如这里配置水平分表时间为202501-203512 ,当insert创建时间字段会自动添加到对应时间点的表中。不会自动创建对应水平分表,如果未创建执行sql会报找不到表错误

第二点:Shardingsphere按create_time字段分表,查询未指定create_time范围,会直接检索所有水平表

如:查询202501-203512所有表,如果有存在未创建的表会直接执行报错

第三点:Shardingsphere需要处理历史数据

方法一:从源数据表按时间归类到对应时间分片表中

方法二:自定义分库分表的算法 可以设置时间节点前查哪个表 时间节点后查哪个分片表

注:查询未指定create_time范围,会直接检索所有水平表是不会触CustomTableShardingAlgorithm的实现方法 ,指定了create_time范围才会触发

yml配置

  shardingsphere:
    mode:
      type: Standalone # Standalone=单机模式或 Cluster、Memory
    datasource:
      names: ds
      ds:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
        username: root
        password: root
    rules:
      sharding:
        tables:
          dp_im_users_info:
            # 定义实际的数据节点范围。这里表示 `dp_im_users_info` 表的数据会根据年份(2025 到 2035)和月份(1 -12 月)分布在不同的节点上
            actual-data-nodes: ds.dp_im_users_info_$->{202501..203512}
            table-strategy:
              standard:
                # 指定分片列为 `create_time`,即根据 `create_time` 列的值进行数据分片
                sharding-column: create_time
                # 指定分片算法的名称,这里使用之前定义的 `tba-monthly`,表示按月进行分片
                sharding-algorithm-name: tba-monthly
        sharding-algorithms:
# 自定义分库分表的算法 这里实现的是按年月份-水平分表不分库
          tba-monthly:
            type: CLASS_BASED
            props:
              strategy: STANDARD
              algorithmClassName: com.shardingsphere.CustomTableShardingAlgorithm

CustomTableShardingAlgorithm.java


import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
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.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class CustomTableShardingAlgorithm implements StandardShardingAlgorithm<Date> {
    private static final DateTimeFormatter TABLE_FORMAT = DateTimeFormatter.ofPattern("yyyyMM");
    private static final Date MIN_VALUE = Date.from(YearMonth.parse("202503", java.time.format.DateTimeFormatter.ofPattern("yyyyMM"))
            .atDay(1).atStartOfDay(ZoneId.of("UTC"))  // 指定时区(可替换为 ZoneId.systemDefault())
            .toInstant());
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
        // 获取分片键值(create_time)
        Date createTime = shardingValue.getValue();
        Date now = new Date();
        if (createTime.before(MIN_VALUE)) {
            createTime = MIN_VALUE;
        }
        else if (createTime.after(now)) {
            createTime = now;
        }
        // 生成表后缀:_202501
        String tableSuffix = DateUtil.format(createTime, TABLE_FORMAT);
        // 组合逻辑表名 + 后缀
        return shardingValue.getLogicTableName() + "_" + tableSuffix;
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {
        Range<Date> range = rangeShardingValue.getValueRange();
        Date start = range.hasLowerBound() ? range.lowerEndpoint() : MIN_VALUE;
        if (start.before(MIN_VALUE)) {
            start = MIN_VALUE;
        }
        Date end = new Date();// range.hasUpperBound() ? range.upperEndpoint() : new Date();
        Set<String> tables = new HashSet<>();
        while (start.before(end)) {
            tables.add(rangeShardingValue.getLogicTableName() + "_" + DateUtil.format(start, TABLE_FORMAT));
            start = addMonth(start, 1);
        }
        return tables;
    }

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

    @Override
    public void init(Properties properties) {
        // 初始化逻辑(可留空)
        System.out.println("===============================");
    }


    /**
     * 返回日期加X月后的日期
     */
    public static Date addMonth(Date date, int i) {
        try {
            String dateStr = DateUtil.format(date,sdf);
            GregorianCalendar gCal = new GregorianCalendar(Integer.parseInt(dateStr.substring(0, 4)),
                    Integer.parseInt(dateStr.substring(5, 7)) - 1, Integer.parseInt(dateStr.substring(8, 10)));
            gCal.add(GregorianCalendar.MONTH, i);
            return gCal.getTime();
        } catch (Exception e) {
            return null;
        }
    }
}

完结撒花!!!

各位侠士就是我的动力,助力下,来个赞吧感谢!!!

各位侠士就是我的动力,助力下,来个赞吧感谢!!!

各位侠士就是我的动力,助力下,来个赞吧感谢!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值