Springboot整合shardingsphere框架实现mysql分表(解决数据表存储压力)

shardingsphere官网:https://shardingsphere.apache.org/

场景:如果表的数据过大,我们可能需要把一张表拆分成多张表,这里就是通过ShardingSphere实现分表功能,但不分库。

快速使用shardingsphere进行分表

1,数据库设计

我这里建了一个名为myqxin的库,来演示这次的分表操作。里面有sx_text_a作为基础表,拆分为三个表进行存储,分表是sx_test_a0,sx_test_a1,sx_test_a2,表结构都是一样的,只是表名不一样

如图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
sx_test_a1与sx_test_a2是上面一样的,就不截图展示了。

2,springboot项目整合shardingsphere

项目架构如果:

在这里插入图片描述
就是一个简单的springboot项目,主要来演示分表操作。

  • 项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.myqxin</groupId>
    <artifactId>itcast-sharding-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <!-- 父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!--SpringMVC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis-plus begin-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!--MySQL连接包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>
        <!--工具包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.2</version>
        </dependency>

        <!-- 本次演示必须的依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
    </dependencies>

</project>

大多是项目所需要的基础依赖,最后两个依赖时本次演示所必须要的

  • application.properties配置文件
server.port=9018

# 数据源 主库
spring.shardingsphere.datasource.names=master
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/myqxin?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=root


# 数据分表规则
# 指定所需分的表
spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{
    
    0..2}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.sharding-column=indexes
# 分表规则为该字段值除以3取模
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.algorithm-expression=sx_test_a$->{
    
    indexes % 3}

# 打印SQL
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true

这里我们只进行对单个库进行分表操作,所以不用多数据源,数据的连接信息我就不作解释了。
解释一下如下内容:

spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{0..2}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.sharding-column=indexes
# 分表规则为该字段值除以3取模
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.algorithm-expression=sx_test_a$->{indexes % 3}
sx_test_a:这个是基础表,这没什么好说的
master.sx_test_a$->{
    
    0..2}:
master这个主数据源节点,这个很好理解。
sx_test_a基础表也不说了。
$->{
    
    0..2}如下图介绍(看完下图,说一下我理解配置与数据库之间的关系:因为$->{
    
    0..2}是区间,由此它的内容加上前面的sx_test_a就组合成了sx_test_a0、sx_test_a1、sx_test_a2。这与我们库里面所分的三个表是一致,这样他们就能通过这个匹配上(根据inline.algorithm-expression=sx_test_a$->{
    
    indexes % 3}规则),将数据插入到匹配对应的表)
indexes:这个理解为你库里面的某个字段
$->{
    
    indexes % 3}如下图介绍(看完下图,说一下我的理解,我们可以把上面的actual-data-nodes=master.sx_test_a$->{
    
    0..2}这个看成一个整体,就是我们总共要插入的表,而$->{
    
    indexes % 3}这个拿着indexes字段的值跟3取模,得到的结果就是我们从整体操作的表中选择指定的一表进行数据的插入)

在这里插入图片描述

在这里插入图片描述

  • SxTestAController.java
package com.myqxin.controller;

import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: myqxin
 * @Desc:
 * @create: 2021-12-21 09:22
 **/
@RestController
@RequestMapping("/sxa")
public class SxTestAController {
    
    

    @Autowired
    private SxTestAService sxTestAService;
    
    @GetMapping("/savetest")
    public String saveTest(){
    
    
        ArrayList<SxTestA> sxTestAS = new ArrayList<>();
        sxTestAS.add(new SxTestA(1,"number1",1,"2020","no","1234.3"));
        sxTestAS.add(new SxTestA(2,"number2",2,"2020","no","14.3"));
        sxTestAS.add(new SxTestA(3,"number3",3,"2020","no","5434.3"));
        sxTestAS.add(new SxTestA(4,"number4",4,"2020","no","334.3"));
        sxTestAS.add(new SxTestA(5,"number5",5,"2020","no","134.3"));
        sxTestAService.saveBatch(sxTestAS);
        return "myqxin";
    }
    
    @GetMapping("/list")
    public ResponseEntity<List<SxTestA>> list(){
    
    
        return ResponseEntity.ok(sxTestAService.list());
    }
}

3,请求测试
  • 发送请求:localhost:9018/sxa/savetest

从控制台可以看到SQL打印语句,内容太多只截取了部分,如图:

在这里插入图片描述

来看数据库的效果:

  • sx_test_a0

在这里插入图片描述

  • sx_test_a1

在这里插入图片描述

  • sx_test_a2

在这里插入图片描述

到此完成分表插入数据

详解:

4,获取刚才插入的所有数据
  • 发送请求:localhost:9018/sxa/list

请求结果如图:

在这里插入图片描述
可以看出我们虽然已经分了表,但它查询所有的时候还是帮我们聚合在了一起

自定义分表规则,实现按照日期月份进行分表

项目还是这个项目,只是配置文件需要做一些更改,规则:在sx_test_a基础表上,以data_time字段的时间进行月份分表,即myqxin这个库里面,有sx_test_a2021_1到sx_test_a2021_12总共12张表,由于表太多了,数据我只取四个月份的数据,表也只建4个,到时候建表的时候可以通过存储过程动态生成表

  • 表如下:

在这里插入图片描述

1,application.properties文件
server.port=9018

# 数据源 主库
spring.shardingsphere.datasource.names=master
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/myqxin?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=root

# 指定所需分的表
spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{2021}_$->{1..4}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.standard.sharding-column=date_time
# 自定义分表规则
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.standard.precise-algorithm-class-name=com.myqxin.utils.TableShardingAlgor


# 打印SQL
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true
2,TableShardingAlgor.java自定义实现类
package com.myqxin.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.logging.SimpleFormatter;

/**
 * @author: myqxin
 * @Desc:
 * @create: 2021-12-22 09:41
 **/
@Slf4j
public class TableShardingAlgor implements PreciseShardingAlgorithm<String> {
    
    

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
    
    
        // 真实节点
        collection.stream().forEach((item) -> {
    
    
            log.info("actual node table:{}",item);
        });

        // 精确分片
        log.info("column value:{}",preciseShardingValue.getValue());

        // 获取基础表名
        String tb_name = preciseShardingValue.getLogicTableName();

        // 根据当前日期来分表
        String format = null;

        try {
    
    
            // 这里的类型是根据你实现PreciseShardingAlgorithm的类型String
            String date = preciseShardingValue.getValue();
            // 我这里需要进行一些处理(不需要的可以忽略)
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M");
            Date parse = sdf.parse(date);
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy_M");
            format = sdf1.format(parse);
        } catch (ParseException e) {
    
    
            e.printStackTrace();
        }

        // 选择表 基础表+分表名称
        tb_name = tb_name + format;
        System.out.println("tb_name: "+tb_name);

        // 进行匹配
        for (String each : collection) {
    
    
            System.out.println("sx_text_a: "+each);
            if (each.equals(tb_name)){
    
    
                // 匹配成功开始对这个表进行数据的插入
                return each;
            }
        }
        throw new IllegalArgumentException();
    }
}

  • SxTestAController.java
package com.myqxin.controller;

import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: myqxin
 * @Desc:
 * @create: 2021-12-21 09:22
 **/
@RestController
@RequestMapping("/sxa")
public class SxTestAController {
    
    

    @Autowired
    private SxTestAService sxTestAService;

    @GetMapping(value = "/readDirectory")
    public String readDirectory(){
    
    
        sxTestAService.readDirectory();
        return "myqxin";
    }
}
  • SxTestAServiceImpl.java
package com.myqxin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myqxin.mapper.SxTestAMapper;
import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author: myqxin
 * @Desc:
 * @create: 2021-12-21 09:21
 **/
@Service
public class SxTestAServiceImpl extends ServiceImpl<SxTestAMapper, SxTestA> implements SxTestAService {
    
    

    @Autowired(required = false)
    private SxTestAMapper sxTestAMapper;

    @Override
    public void readDirectory() {
    
    
        String path = "C:\\Users\\Administrator\\Desktop\\pams";
        File file = new File(path);
        if (!file.isDirectory()) {
    
    
            System.err.println(file.getPath() + ": " + file.getName());
        } else if (file.isDirectory()) {
    
    
            String[] list = file.list();
            for (int i = 0; i < list.length; i++) {
    
    
                ArrayList<SxTestA> sxMonitors = new ArrayList<>();
                File file1 = new File(path + "\\" + list[i]);
                try {
    
    
                    String s = FileUtils.readFileToString(new File(file1.getPath()), "utf-8");
                    String[] lonList = s.split("\r|\n|\\s+");
                    for (int i1 = 0; i1 < lonList.length; i1++) {
    
    
                        SxTestA sxMonitor = new SxTestA();
                        String[] split = file1.getName().split("\\_|\\.");
                        sxMonitor.setNumber(file1.getName());
                        sxMonitor.setIndexes(i1);

                        String taskTime = split[1];
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HH");
                        Date date = sdf.parse(taskTime);
                        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String sr = df.format(date);
                        sxMonitor.setDateTime(sr);
                        sxMonitor.setMonitorName(split[0]);
                        sxMonitor.setMonitorValue(lonList[i1]);
                        sxMonitors.add(sxMonitor);
                    }
                    System.err.println(sxMonitors.size()); //13345
                    // TODO 数据批处理
                    saveBatch(sxMonitors);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public List<SxTestA> selectAll() {
    
    
        QueryWrapper<SxTestA> wrapper = new QueryWrapper<>();
        //wrapper.eq("date_time",timeStamp);
        return list();
    }
}

数据是我自己造的,你们根据自己建的表,造一些数据进行模拟

3,请求测试
  • 发送请求:localhost:9018/sxa/readDirectory

数据库效果如下图:

sx_test_a2021_1

在这里插入图片描述
sx_test_a2021_2

在这里插入图片描述
sx_test_a2021_3

在这里插入图片描述

我这里就只展示三个,这里的效果已经实现了

常见问题及解决方式

  • 错误信息1:

在这里插入图片描述
原因是我们使用shardingsphere框架进行分表的时候,配置文件里面没有加
spring.main.allow-bean-definition-overriding=true

spring.main.allow-bean-definition-overriding=true

在我们的配置文件加上就可以解决了

在这里插入图片描述

  • 错误信息2:

在这里插入图片描述
原因是你当前使用的mysql版本与依赖导入的版本不一致造成的,更改为一致就行了,因为我装的是mysql8.0.27版本

在这里插入图片描述
所以引入的数据库连接依赖为8.0.27

   	   <!--MySQL连接包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>

这次我演示的项目是用的8.0.13的版本,所以引入的依赖也是8.0.13,你们根据实际使用的版本进行修改

动态创建数据库表

本次的案例,我们是在基础表上自己一个一个创建的,这样是不符合实际业务需求的,我们要将建表的操作交给程序,动态的生成我们想要的表(建议写个定时任务,来执行这个建表的程序)。我已经整理好了,点击去查看吧动态创建数据库表

猜你喜欢

转载自blog.csdn.net/qq_45752401/article/details/122105085