Springboot integra a estrutura shardingsphere para implementar subtabelas mysql (resolver a pressão de armazenamento das tabelas de dados)

Site oficial do Shardingsphere: https://shardingsphere.apache.org/

Cenário: Se os dados na tabela forem muito grandes, poderemos precisar dividir uma tabela em várias tabelas. Aqui, a função de divisão de tabela é implementada por meio do ShardingSphere, mas não do banco de dados.

Use rapidamente o shardingsphere para fragmentação de tabela

1. Projeto de banco de dados

Construí uma biblioteca chamada myqxin aqui para demonstrar essa operação de divisão de tabela. Existe sx_text_a como tabela básica, que é dividida em três tabelas para armazenamento. As subtabelas são sx_test_a0, sx_test_a1, sx_test_a2. As estruturas das tabelas são as mesmas, mas os nomes das tabelas são diferentes.

Como mostrado na imagem:

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
sx_test_a1 e sx_test_a2 são iguais aos anteriores, portanto, nenhuma captura de tela é mostrada.

2. Projeto Springboot integra shardingsphere

Estrutura do projeto se:

Insira a descrição da imagem aqui
É um projeto springboot simples, principalmente para demonstrar a operação de divisão de tabelas.

  • Dependências do projeto
<?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>

A maioria delas são dependências básicas exigidas pelo projeto. As duas últimas dependências são necessárias para esta demonstração.

  • arquivo de configuração 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

Aqui realizamos apenas operações de divisão de tabelas em um único banco de dados, portanto não há necessidade de múltiplas fontes de dados. Não explicarei as informações de conexão de dados.
Explique o seguinte:

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取模,得到的结果就是我们从整体操作的表中选择指定的一表进行数据的插入)

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

  • 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. Solicite um teste
  • Enviar solicitação: localhost:9018/sxa/savetest

Você pode ver a instrução SQL print no console. Há muito conteúdo e apenas parte dele foi interceptada, conforme mostrado na figura:

Insira a descrição da imagem aqui

Vejamos o efeito do banco de dados:

  • sx_test_a0

Insira a descrição da imagem aqui

  • sx_test_a1

Insira a descrição da imagem aqui

  • sx_test_a2

Insira a descrição da imagem aqui

Isso conclui a inserção de dados na subtabela.

Explicação detalhada:

4. Obtenha todos os dados que acabou de inserir
  • Enviar solicitação: localhost:9018/sxa/list

O resultado da solicitação é mostrado na figura:

Insira a descrição da imagem aqui
Pode-se observar que embora tenhamos dividido as tabelas, isso ainda nos ajuda a agregá-las ao consultar todas

Personalize regras de divisão de tabelas para implementar a divisão de tabelas com base em data e mês

O projeto ainda é este projeto, mas o arquivo de configuração precisa fazer algumas alterações. Regras: Na tabela básica sx_test_a, use o tempo do campo data_time para dividir as tabelas em meses. Ou seja, na biblioteca myqxin, há um total de 12 tabelas de sx_test_a2021_1 a sx_test_a2021_12, porque há muitas tabelas. Sim, pego apenas quatro meses de dados e crio apenas 4 tabelas. Quando a tabela é criada, a tabela pode ser gerada dinamicamente por meio de um procedimento armazenado.

  • A tabela é a seguinte:

Insira a descrição da imagem aqui

1. arquivo 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. Classe de implementação personalizada 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();
    }
}

Eu mesmo criei os dados. Você pode criar alguns dados para simulação com base nas tabelas que você criou.

3. Solicite um teste
  • Enviar solicitação: localhost:9018/sxa/readDirectory

O efeito do banco de dados é o seguinte:

sx_test_a2021_1

Insira a descrição da imagem aqui
sx_test_a2021_2

Insira a descrição da imagem aqui
sx_test_a2021_3

Insira a descrição da imagem aqui

Mostrarei aqui apenas três. O efeito aqui foi alcançado.

Perguntas frequentes e soluções

  • Mensagem de erro 1:

Insira a descrição da imagem aqui
A razão é que quando usamos a estrutura shardingsphere para fragmentação de tabela,
spring.main.allow-bean-definition-overriding=true não é adicionado ao arquivo de configuração.

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

Isso pode ser resolvido adicionando-o ao nosso arquivo de configuração

Insira a descrição da imagem aqui

  • Mensagem de erro 2:

Insira a descrição da imagem aqui
O motivo é que a versão do mysql que você está usando atualmente é inconsistente com a versão que depende da importação, basta alterá-la para ficar consistente, pois instalei a versão mysql8.0.27.

Insira a descrição da imagem aqui
Portanto, a dependência de conexão com o banco de dados introduzida é 8.0.27

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

O projeto que estou demonstrando desta vez usa a versão 8.0.13, portanto as dependências introduzidas também são 8.0.13. Você pode modificá-las de acordo com a versão real utilizada.

Crie tabelas de banco de dados dinamicamente

Neste caso, criamos um por um nas tabelas básicas. Isso não atende às reais necessidades do negócio. Precisamos entregar a operação de criação de tabelas ao programa e gerar dinamicamente as tabelas que desejamos (recomenda-se escrever um tarefa agendada para executar este programa de criação de tabela). Já resolvi. Clique para visualizar. Crie tabelas de banco de dados dinamicamente.

Acho que você gosta

Origin blog.csdn.net/qq_45752401/article/details/122105085
Recomendado
Clasificación