后端——》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次),然后把查到的结果返回给我。如果查大量数据的情况下,每个表都这样执行一次,速度会有提升吗?

相关推荐
javaDocker32 分钟前
业务架构、数据架构、应用架构和技术架构
架构
新知图书36 分钟前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
盛夏绽放1 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
Ares-Wang1 小时前
Asp.net Core Hosted Service(托管服务) Timer (定时任务)
后端·asp.net
JosieBook2 小时前
【架构】主流企业架构Zachman、ToGAF、FEA、DoDAF介绍
架构
Rverdoser2 小时前
RabbitMQ的基本概念和入门
开发语言·后端·ruby
Tech Synapse3 小时前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
.生产的驴3 小时前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
微信-since811923 小时前
[ruby on rails] 安装docker
后端·docker·ruby on rails