后端——》springboot+jpa+shardingsphere实现分表

写在前面

最近有一个业务,需要数据库实现10亿级别数据的存取。用到了shardingsphere分表,网上文章鱼龙混杂,按照网上的pom和yml配置,项目老半天跑不起来,或者运行图中报错。将实现的经过和遇到的坑在此记录一下。

实现步骤

建表

此次只分表,不分库。所以在一个数据库下我们先新建一个表,要有id,主键自增。然后我们把表copy100份。

如图:rainbow_mobile是主表,rainbow_mobile_copy0 到 rainbow_mobile_copy100 是实际存数据的从表

此处有一个小细节,100份从表的表名 并不是 从 1到100,而是从0到99,为什么要这样分,配合下面的内容就能看明白。

那主表(rainbow_mobile)跟从表(rainbow_mobile_copy0到rainbow_mobile_copy99)是什么关系呢,分别是做什么的呢。

主表只是声明了表结构,供后端框架 提供映射关系,并不存储数据。而从表是真正存储数据的表。

pom文件

这个shardingsphere的坑实在是太多,版本号又乱,各种启动starter配合起来也乱。稍微一个版本不对就启动报错,试了很久,下面这个是ok的,直接复制粘贴使用。

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.6.0</version> <!-- 根据你的Spring Boot版本设置版本号 -->
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>2.6.0</version> <!-- 根据你的Spring Boot版本设置版本号 -->
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version> <!-- 根据你的MySQL连接器版本设置版本号 -->
    </dependency>
    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>2.6.0</version> <!-- 根据你的Spring Boot版本设置版本号 -->
        <scope>test</scope>
    </dependency>
    <!-- ShardingSphere -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.0.0-alpha</version>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-crypto</artifactId>
        <version>5.7.8</version> <!-- 使用你需要的版本 -->
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.18</version>
    </dependency>

</dependencies>

application.yml 配置文件

yml 复制代码
server:
  port: 8080
spring:
  jpa:
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        dialect: org.hibernate.dialect.MySQL5Dialect
        show_sql: true
        ddl-auto: create-drop
  shardingsphere:
    datasource:  #数据源配置
      names: ds0
      common:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.zaxxer.hikari.HikariDataSource
      ds0:
        jdbc-url: jdbc:mysql://127.0.0.1:3306/md5rainbow?serverTimezone=UTC&useSSL=false
        username: root
        password: 123456
    rules:
      sharding:
        sharding-algorithms:  #分片算法配置
          table-inline:
            type: INLINE
            props: 
              algorithm-expression: rainbow_mobile_copy$->{ id % 100 }
        key-generators:  #主键生成策略
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 1
        tables:  #分表策略
          rainbow_mobile:
            actual-data-nodes: ds0.rainbow_mobile_copy$->{0..99}
            table-strategy:
              standard:
                sharding-column: id
                sharding-algorithm-name: table-inline
    props:
      sql-show: true

这里面除了常规的配置外,最主要的配置是这三个:

  1. 上文第11行的ddl-auto: create-drop,这个配置会配合项目启动时建立的hibernate_sequence表完成主键自增配置
  2. 上文第28行的algorithm-expression:rainbow_mobile_copy$->{ id % 100 },这是一个落库算法,根据id取馍。余数为1,就落到rainbow_mobile_copy1表;余数为99,就落到rainbow_mobile_copy99表;余数为0,就落到rainbow_mobile_copy0表(通常id为100的倍数时,就会落到rainbow_mobile_copy0表中,由于余数不可能为100,所以数据库中不用添加rainbow_mobile_copy100这个表
  3. 上文第36行的actual-data-nodes: ds0.rainbow_mobile_copy$->{0..99},这个表示数据库中的表名的占位符 是从 第0到99的。(rainbow_mobile_copy是我的表,jym可以替换成自己的表)

Repository

这没啥好说的 ,平平无奇

java 复制代码
import com.x.md5.entity.RainbowMobile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RainbowMobileRepository extends JpaRepository<RainbowMobile, Long> {

}

Entity

这个是实体类,这个就有说法了。在普通的jpa或者springboot项目中,id上的自增长注解我们通常选用的是 IDENTITY这个选项。他们的区别是:

  • AUTO:由JPA自动选择适合底层数据库的主键生成策略。对于MySQL数据库,它会使用自增长字段来生成主键。

  • IDENTITY:使用底层数据库的自增长字段来生成主键。只适用于支持自增长字段的数据库,如MySQL和SQL Server等。

  • SEQUENCE:使用底层数据库的序列来生成主键。只适用于支持序列的数据库,如Oracle和PostgreSQL等。

  • TABLE:使用一个特定的数据库表来存储主键值。它是一种通用的主键生成策略,适用于所有支持JDBC的数据库。

由于我们在配置文件中配置了主键生成策略和分片算法,所以此处我们就不能选IDENTITY了,而要选AUTO了,由jpa自动选择。

java 复制代码
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;

@Getter
@Setter
@ToString
@Table(name = "rainbow_mobile")
@Entity
public class RainbowMobile {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String mobile;

    private String mobileMd5;
}

Controller

配置已完毕,我们写个test来测试一下

往数据中添加1000条数据

java 复制代码
import cn.hutool.crypto.SecureUtil;
import com.x.md5.entity.RainbowMobile;
import com.x.md5.service.RainbowMobileService;
import org.springframework.beans.factory.annotation.Autowired;
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;

@RestController
@RequestMapping("test")
public class TestController {
    
    @Autowired
    RainbowMobileService rainbowMobileService;

    @PostMapping(value = "test")
    public void test() {
        String mobileLeft="1531753";
        List<RainbowMobile> list=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            String suffix = String.format("%04d", i);
            String mobile=mobileLeft+""+suffix;
            RainbowMobile rainbowMobile =new RainbowMobile();
            rainbowMobile.setMobile(mobile);
            rainbowMobile.setMobileMd5(SecureUtil.md5(mobile));
            rainbowMobileService.save(rainbowMobile);
        }
    }
}

运行项目,系统会自动创建一个账 hibernate_sequence表(更新id),我们给他设置一个初始值

js 复制代码
update hibernate_sequence set next_val=1

然后调用上面的 test接口。然后查看数据库,选3张表来验证插入结果。

可以看到数据已经按照要求正确的分布在了各个表中。

查询

上面是写入的代码,查询就直接调用jpa的Repository中的 findByXXX就好了。

从控制台输出看 我们查询一条数据,它会自动执行 100条select语句(数据库中有100张表,它select 了100次),然后把查到的结果返回给我。如果查大量数据的情况下,每个表都这样执行一次,速度会有提升吗?

相关推荐
Chrikk10 分钟前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*13 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue13 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man16 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml43 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠4 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
AskHarries4 小时前
Java字节码增强库ByteBuddy
java·后端