采用Sharding-Jdbc对mysql 读写分离、分库分表

采用Sharding-Jdbc对mysql 读写分离、分库分表

一、ShardingSphere介绍

ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar这3款相互独立的产品组成。他们均提供标准化的数据分片、分布式事务 和 数据库治理功能,可满足各种多样化的应用场景。

官网:http://shardingsphere.apache.org/index_zh.html

SpringBoot配置地址:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/

行表达式地址:https://shardingsphere.apache.org/document/current/cn/features/sharding/concept/inline-expression/

二、Sharding-Jdbc与MyCat区别

MyCat是一个基于第三方应用中间件数据库代理框架,客户端所有的jdbc请求都必须要先交给MyCat,再有MyCat转发到具体的真实服务器中。Sharding-Jdbc是一个Jar形式,在本地应用层重写Jdbc原生的方法,实现数据库分片形式。MyCat属于服务器端数据库中间件,而Sharding-Jdbc是一个本地数据库中间件框架。

三、什么时候分表分库?

单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
摘自:阿里巴巴java开发手册

四、Sharding-Jdbc 实现读写分离

  1. mysql 主从复制
    配置读写分离必须依托于mysql的主从复制,主从复制的配置可参考我另一篇博客:

    https://blog.csdn.net/qq_43692950/article/details/107731431

  2. 新建SpringBoot项目,配置pom

        <!-- sharding -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
        
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        
        <!--阿里数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>
        
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
  1. application.yml
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      names:
        master,slave
      # 主数据源
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.1:3306/db1?characterEncoding=utf-8
        username: root
        password: root
      # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.2:3306/db2?characterEncoding=utf-8
        username: root
        password: root
    masterslave:
      # 读写分离配置  ROUND_ROBIN(轮询),RANDOM(随机)
      load-balance-algorithm-type: round_robin
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: slave
    props:
      # 开启SQL显示,默认false
      sql:
        show: true

其中 load-balance-algorithm-type为有多个数据库时的规则,可选择轮询或随机。props.sql.show=true为开起日志,会打印当前使用的数据库和sql。

  1. 实体类
@Data
@TableName("user")
public class UserEntity {
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;
    private String username;
    private String passwd;
    
    public UserEntity(){
    }
    public UserEntity(String username, String passwd) {
        this.username = username;
        this.passwd = passwd;
    }
}

  1. Mapper
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
}
  1. 使用
@RestController
public class TestController {
    @Autowired
    UserMapper userMapper;

    @GetMapping("/AddUser/{username}/{passwd}")
    public String AddUser(@PathVariable("username")String username,@PathVariable("passwd")String passwd){
        if (userMapper.insert(new UserEntity(username,passwd)) > 0){
            return "Success";
        }
        return "Fail";
    }

    @GetMapping("/GetAllUser")
    public List<UserEntity> GetAllUser(){
        return userMapper.selectList(null);
    }

    @GetMapping("/GetMasterAllUser")
    public List<UserEntity> GetMasterAllUser(){
        HintManager.getInstance().setMasterRouteOnly();
        return userMapper.selectList(null);
    }
}

其中 HintManager.getInstance().setMasterRouteOnly();为强制使用主表查询,因为在有些时候可能因为网络的震荡,主数据库的数据同步给从数据库产生延时,对数据插入后紧接着要查询的情况下可以使用这个api强制主数据库查询。

  1. 接着调用上述接口可以看到打印的日志,在插入时走的master,查询时走的slave,强制使用主表查询的日志为master。
    在这里插入图片描述

五、Sharding-Jdbc 实现单库分表拆分

下面演示分表分库规则都是根据id取模计算的。下面实现根据id %3,将数据均匀的放到一个数据库中的三个表中,实现数据的分表操作。

  1. 为演示功能先在数据库建三个简单的测试表 user_info_0,user_info_1,user_info_2, 表里只有id,name两个字段。
    在这里插入图片描述
  2. application.yml
server:
  port: 8082

#单库水平分割
spring:
  main:
    allow-bean-definition-overriding: true

  shardingsphere:
    datasource:
      names: db1  # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
      # 配置数据源具体连接内容
      db1:
        url: jdbc:mysql://192.168.1.1:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
    # 配置表的分布,表的策略
    sharding:
      tables:
        user_info:
          actual-data-nodes: db1.user_info_$->{0..2}
          # 指定user_info表 主键id 生成策略为 SNOWFLAKE
          key-generator:
            column: id
            type: SNOWFLAKE

          # 指定分片策略 约定id % 3 确定存入哪张表
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: user_info_$->{id % 3 }

    # 打开sql输出日志
    props:
      sql:
        show: true
  1. 实体类
@Data
public class UserInfo {
    private int id;
    private String name;
    public UserInfo(){}

    public UserInfo(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
  1. Mapper
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
  1. 使用
@RestController
public class TestController {

    @Autowired
    UserInfoMapper userInfoMapper;

    @GetMapping("/AddUser/{id}/{name}")
    public String AddUser(@PathVariable("id")int id,@PathVariable("name")String name){
       if (userInfoMapper.insert(new UserInfo(id,name))>0){
            return "Success";
       }
       return "Fail";
    }

    @GetMapping("/FindUsers")
    public Object FindUser(){
        return userInfoMapper.selectList(null);
    }

    @GetMapping("/FindUser/{id}")
    public Object FindUser(@PathVariable("id")int id){
        return userInfoMapper.selectById(id);
    }
}

5.测试

当请求:http://localhost:8082/AddUser/0/test时,打印日志:
在这里插入图片描述

查看user_info_0表中的数据:

在这里插入图片描述
当请求:http://localhost:8082/AddUser/2/test时,打印日志:
在这里插入图片描述
查看user_info_2表中的数据:

在这里插入图片描述
以此类推,根据id%3,将数据放置不同的表同。

当执行查询时:
在这里插入图片描述
会自动将所有的数据规整为一起,但最好不要查询全部,他会分别查询这三个表然后合到一起返回,数据量大时效率比较低,最好根据id查询。

以上便实现数据分表,下面实现分库单表。

五、Sharding-Jdbc 实现分库水平拆分

下面实现根据id %2,将数据均匀的放到两个个数据库中的user_info表中,实现数据的分库操作。

  1. 新建db1,db2数据库,库中各建user_info表,表中字段均为id,name。
    在这里插入图片描述
  2. application.yml
#多库水平分割
spring:
  main:
    allow-bean-definition-overriding: true

  shardingsphere:
    datasource:
      names: db1,db2  # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
      # 配置数据源具体连接内容
      db1:
        url: jdbc:mysql://192.168.1.2:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
      db2:
        url: jdbc:mysql://192.168.1.1:3306/db2?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver

    # 配置库分布,库的策略
    sharding:
      tables:
        user_info:
          actual-data-nodes: db$->{1..2}.user_info
          # 指定user_info表 主键id 生成策略为 SNOWFLAKE
          key-generator:
            column: id
            type: SNOWFLAKE

          database-strategy:
            inline:
              sharding-column:  id
              algorithm-expression: db$->{id % 2 +1}

    # 打开sql输出日志
    props:
      sql:
        show: true
  1. 实体类和Mapper及Controller和上面单裤多表使用相同。
  2. 测试
    当访问:http://localhost:8082/AddUser/0/test0 时,打印日志:
    在这里插入图片描述
    查看db1中的数据为:

在这里插入图片描述
当访问:http://localhost:8082/AddUser/1/test1 时,打印日志:

在这里插入图片描述
查看db2中的数据为:

在这里插入图片描述
以此类推,数据会根据id%2,均匀的放到两个不同的数据库中,实现分库的想过。

当进行查询时,同样也会合并为一起返还给调用者:

在这里插入图片描述
上面便实现了,分库的效果,但有时候数据量会非常大,使用在一个库中分n张表,或分n个相同的库确实也可以解决,但维护的成本会相应增大,不如使用多库多表的方式,有多个数据库,每个库中又有多个表。

六、Sharding-Jdbc 实现多库多表拆分

建立两个数据库db1,db2,每个数据库中包含三张表(注意:如果根据id取模的方式分库分表,库的个数和表的个数不要同为偶数或同为奇数,一偶一奇才能均匀分布)

  1. 库:db1,db2,表:user_info_0,user_info_1,user_info_2,字段:id,name。
    在这里插入图片描述
  2. application.yml
#单库水平分割
spring:
  main:
    allow-bean-definition-overriding: true

  shardingsphere:
    datasource:
      names: db1,db2  # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
      # 配置数据源具体连接内容
      db1:
        url: jdbc:mysql://192.168.1.2:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
      db2:
        url: jdbc:mysql://192.168.1.1:3306/db2?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver

    # 配置库和表的分布,库和表的策略
    sharding:
      tables:
        user_info:
          actual-data-nodes: db$->{1..2}.user_info_$->{0..2}
          # 指定user_info表 主键id 生成策略为 SNOWFLAKE
          key-generator:
            column: id
            type: SNOWFLAKE
          #数据库的策略
          database-strategy:
            inline:
              sharding-column:  id
              algorithm-expression: db$->{id % 2 +1}
          #表的策略
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: user_info_$->{id % 3 }

    # 打开sql输出日志
    props:
      sql:
        show: true
  1. 实体类和Mapper及Controller和上面单裤多表使用相同。
  2. 当执行下面接口时:
    http://localhost:8082/AddUser/0/test0
    http://localhost:8082/AddUser/1/test1
    http://localhost:8082/AddUser/2/test2
    http://localhost:8082/AddUser/3/test3
    http://localhost:8082/AddUser/4/test4
    http://localhost:8082/AddUser/5/test5

控制台打印的日志为:

在这里插入图片描述
在这里插入图片描述
数据库中的结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
多库多表的分库分表效果便实现了,从实现的过程可以看出,Sharding-jdbc的配置已经非常简单了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小毕超

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值