SpringBoot集成Sharding-JDBC实现分库分表

本文已收录于专栏 《中间件合集》


目录

版本介绍

SpringBoot的版本是: 2.3.12

ShardingSphere-jdbc的版本是:5.1.2

Mysql-Connector的版本是:8.0.19

Druid的版本是:1.2.8

背景介绍

分库分表是数据库设计中的一种常见策略,主要用于解决在高并发、大数据量场景下的性能瓶颈和管理问题。本文章采用Sharding-JDBC以水平分配的方式来实现分库分表。

ShardingSphere官网:https://shardingsphere.apache.org/document/5.1.2/cn/overview/

分库分表的原因:

  1. 性能提升:单一数据库在高并发情况下容易发生性能瓶颈,分库分表可以将请求分散到多个数据库或表上,降低单个数据库的压力。
  2. 容量管理:随着数据量的增长,单个数据库的存储能力可能不足。分库分表可以将数据分散到多个数据库中,以支撑更大的数据存储需求。
  3. 效率提升:在查询时,分表可以将查询范围缩小,只需要访问更少的数据,从而提高查询效率。
  4. 可维护性:分库分表可以更方便地进行数据备份、恢复和维护,提高系统的整体可维护性。
  5. 横向扩展:分库分表支持横向扩展,允许在现有架构中增加更多的数据库服务器,以适应业务增长。

拆分方式

1. 分库

垂直分库:将不同功能模块或业务逻辑的数据存储在不同的数据库中。例如,用户数据和订单数据分别存放在不同的数据库中。

水平分库:类似于分表,但是将同一类型的数据分散到不同的数据库中。例如,将用户数据按ID范围分散到不同的数据库。
2. 分表

按范围分表:根据某个字段(如时间、ID等)的范围,将数据划分到不同的表中,每个表存储特定范围的数据。

按哈希分表:对某个字段(如用户ID)进行哈希运算,根据哈希值将数据分配到不同的表中。

集成并测试

1.引入依赖

xml 复制代码
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>
        
        <!-- Database Driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.1.2</version>
        </dependency>

2.创建库和表

在每个数据库中都需要创建多个相同的表,例如order_info_0、order_info_2、order_info_3等等,这样在配置分片规则的时候才会根据不同的计算方式映射到不同的库和表里面。

3.pom文件配置

xml 复制代码
server:
  port: 8081
spring:
  autoconfigure: # 排除druid 否则报错
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  shardingsphere:
    # 开启sql打印
    enabled: true
    props:
      # 是否显示sql
      sql-show: true
    datasource:
      #数据源名称
      names: sharding0,sharding1
      #数据源实例: 如果这里还有master
      sharding0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.cj.jdbc.Driver
        #使用Druid,不能使用jdbc-url 得使用url
        url: jdbc:mysql://localhost:3307/budget-lim-dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      sharding1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/budget-lim-dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
    #分片规则
    rules:
      sharding:
        #对表进行分片
        tables:
          #逻辑表名,代表的是需要分表的名称
          order_info:
            #实际节点:这里代表的是 会使用sharding数据源中 order_info表 细分为0~3 4个表
            actual-data-nodes: sharding$->{0..1}.order_info_$->{0..1}
            #库策略
            database-strategy:
              standard:
                #根据num来进行分库
                sharding-column: num
                sharding-algorithm-name: alg_round-robin
            #表策略
            table-strategy:
              #标准表策略
              standard:
                #分表的列
                sharding-column: id
                #分片算法名称: 来源于下面的sharding-algorithms
                sharding-algorithm-name: alg_random
            key-generate-strategy: # 主键生成策略
              column: id  # 主键列
              key-generator-name: snowflake  # 策略算法名称(推荐使用雪花算法)
        #主键生成规则,SNOWFLAKE 雪花算法
        key-generators:
          snowflake:
            type: SNOWFLAKE
        #分片算法
        sharding-algorithms:
          alg_hash_mod:
            #类型:hash取余  类似于获取一个列的数
            type: HASH_MOD
            #分片的数量,因为是2个表,所以是2
            props:
              sharding-count: 2

mybatis:
  #映射文件 配置之后,mybatis会去扫描该路径下的xml文件,才会与Mapper对应起来
  mapper-locations: classpath:mapper/*.xml
  #别名类(实体类)所在包
  type-aliases-package: com.wzil.simplesharding.entity
  configuration:
    #驼峰转换
    map-underscore-to-camel-case: true

4.编写测试类

Entity层

java 复制代码
/**
 * @Author: wzil
 * @DATE: 2024年7月24日
 * @Description:
 **/
@Data
public class OrderInfo {
    private Long id;
    private String name;
    private Integer num;
    private Date createTime;
}

Mapper接口

java 复制代码
/**
 * @Author: wzil
 * @DATE: 2024年7月24日15:26:30
 * @Description: 创建mapper接口,
 **/

@Mapper
public interface OrderInfoMapper {

    List<OrderInfo> list();

    void save(OrderInfo orderInfo);

    void deleteById(@Param("id") Long id);

    void updateNameById(@Param("id") Long id, @Param("name") String name);

    OrderInfo getById(@Param("id") Long id);

    List<OrderInfo> limitOrder();

    List<OrderInfo> limitOrderWithOffset();

}

MapperXML文件

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:命名空间,用来映射对应的mapper
相当于将mapper和mapper.xml连接起来,这一步很重要-->
<mapper namespace="com.wzil.simplesharding.mapper.OrderInfoMapper">
    <insert id="save">
        insert into order_info(`name`,num,create_time) values(#{name},#{num},#{createTime})
    </insert>
    <update id="updateNameById">
        update order_info set name=#{name}
        where id=#{id}
    </update>
    <delete id="deleteById">
        delete from order_info where id=#{id}
    </delete>
    
    <select id="list" resultType="com.wzil.simplesharding.entity.OrderInfo">
        select * from order_info
    </select>
    <select id="getById" resultType="com.wzil.simplesharding.entity.OrderInfo">
        select * from order_info
        where id=#{id}
    </select>
    <select id="limitOrder" resultType="com.wzil.simplesharding.entity.OrderInfo">
        select * from order_info
        order by num desc
        limit 10
    </select>
    <select id="limitOrderWithOffset" resultType="com.wzil.simplesharding.entity.OrderInfo">
        select * from order_info
        order by num desc
        limit 2,10
    </select>
</mapper>

测试类

java 复制代码
@Slf4j
@SpringBootTest
class SimpleShardingApplicationTests {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Test
    void add() {
        for (int i = 0; i < 100; i++) {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setName("wzil"+i);
            orderInfo.setCreateTime(new Date());
            orderInfo.setNum(i);
            orderInfoMapper.save(orderInfo);
        }
    }

    @Test
    void delete() {
        Long id=1022869460031111169L;
        orderInfoMapper.deleteById(id);
    }

    @Test
    void update() {
        Long id=1022869460031111169L;
        String name="hello";
        orderInfoMapper.updateNameById(id,name);
    }

    @Test
    void list() {
        List<OrderInfo> list = orderInfoMapper.list();
        System.out.println(list);
    }

    @Test
    void getById() {
        Long id=1022869460031111169L;
        OrderInfo info=orderInfoMapper.getById(id);
        System.out.println(info);
    }
}

5.运行结果

此实例运行的是更新的语句,可以看到是对sharding1数据库中的order_info_0进行更新操作的。

自定义分片规则

定义分片类

java 复制代码
package com.wzil.simplesharding.config;

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.util.Collection;
import java.util.Properties;

/**
 * @BelongsProject: simple_sharding
 * @BelongsPackage: com.wzil.simplesharding.config
 * @Author: Wuzilong
 * @Description: 轮询分片算法
 * @CreateTime: 2024-07-24 14:21
 * @Version: 1.0
 */

public class RoundRobinShardingAlgorithm implements StandardShardingAlgorithm<String> {

    int currentIndex=0;
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
        // 计算当前的索引位置
        currentIndex = (currentIndex + 1) % collection.size();
        // 根据当前索引返回数据源名称
        return (String) collection.toArray()[currentIndex];
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
        return null;
    }

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

    @Override
    public void init(Properties properties) {

    }
}

编写一个分片规则的类去实现StandardShardingAlgorithm接口,去重写doSharding方法。doSharding(Collection collection, PreciseShardingValue preciseShardingValue)这个方法是确定值的分片规则。 doSharding(Collection collection, RangeShardingValue rangeShardingValue)这个方法是范围值的分片规则。根据业务的需求来重写不同的方法。

编写pom文件

xml 复制代码
        #分片算法
        sharding-algorithms:
          alg_round-robin:
            #指定了算法的类型
            type: CLASS_BASED
            props:
              #标准的分片策略
              strategy: standard
              #算法类的全限定名
              algorithmClassName: com.wzil.simplesharding.config.RoundRobinShardingAlgorithm

编写完之后就可以在上面配置的分库或者分表的规则去指定我们自己定义的alg_round-robin这个类型了。

总结提升

分库分表是一种有效的数据库设计策略,能够应对高并发和大数据量的挑战。尽管其带来了复杂性和维护成本,但在许多实际应用中,以提升性能和可扩展性为目标的分库分表依然是一个广泛采用的解决方案。在实施之前,需要深入分析业务需求和技术架构,合理设计分库分表策略,以便最大化其效益。

🎯 此文章对你有用的话记得留言+点赞+收藏哦🎯

相关推荐
秋意钟7 分钟前
Spring新版本
java·后端·spring
椰椰椰耶9 分钟前
【文档搜索引擎】缓冲区优化和索引模块小结
java·spring·搜索引擎
mubeibeinv11 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
青莳吖12 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall19 分钟前
期末考学C
java·开发语言
重生之绝世牛码21 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行27 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Allen Bright35 分钟前
Spring Boot 整合 RabbitMQ:手动 ACK 与 QoS 配置详解
spring boot·rabbitmq·java-rabbitmq
A小白59081 小时前
Docker部署实践:构建可扩展的AI图像/视频分析平台 (脱敏版)
后端
新手小袁_J1 小时前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11