zoukankan      html  css  js  c++  java
  • Spring Boot demo系列(十二):ShardingSphere + MyBatisPlus 读写分离 + 主从复制

    1 概述

    本文讲述了如何使用MyBatisPlus+ShardingSphere进行读写分离,以及利用MySQL进行一主一从的主从复制。

    具体步骤包括:

    • MySQL主从复制环境准备(Docker
    • 搭建ShardingShpere+MyBatisPlus+Druid环境
    • 测试

    2 环境

    • OpenJDK 11.0.11
    • Spring Boot 2.5.1
    • MyBatis Plus 3.4.3.1
    • MyBatis Plus Generator 3.5.0
    • Druid 1.2.6
    • ShardingSphere 4.1.1
    • MySQL 8.0.25

    3 一些基础理论

    3.1 读写分离

    读写分离,顾名思义就是读和写分开,更具体来说,就是:

    • 写操作在主数据库进行
    • 读操作在从数据库进行

    使用读写分离的根本目的就是为了提高并发性能,如果读写都在同一台MySQL上实现,相信会不如一台MySQL写,另外两台MySQL读这样的配置性能高。另一方面,在很多时候都是读操作的请求要远远高于写操作,这样就显得读写分离非常有必要了。

    3.2 主从复制

    主从复制,顾名思义就是把主库的数据复制到从库中,因为读写分离之后,写操作都在主库进行,但是读操作是在从库进行的,也就是说,主库上的数据如果不能复制到从库中,那么从库就不会读到主库中的数据。严格意义上说,读写分离并不要求主从复制,只需要在主库写从库读即可,但是如果没有了主从复制,读写分离将失去了它的意义。因此读写分离通常与主从复制配合使用。

    因为本示例使用的是MySQL,这里就说一下MySQL主从复制的原理,如下图所示:

    在这里插入图片描述

    工作流程如下:

    • 主库修改数据后,将修改日志写入binlog
    • 从库的I/O线程读取主库的binlog,并拷贝到从库本地的binlog
    • 从库本地的binlogSQL线程读取,执行其中的内容并同步到从库中

    3.3 数据库中间件简介

    数据库中间件可以简化对读写分离以及分库分表的操作,并隐藏底层实现细节,可以像操作单库单表那样操作多库多表,主流的设计方案主要有两种:

    • 服务端代理:需要独立部署一个代理服务,该代理服务后面管理多个数据库实例,在应用中通过一个数据源与该代理服务器建立连接,由该代理去操作底层数据库,并返回相应结果。优点是支持多语言,对业务透明,缺点是实现复杂,实现难度大,同时代理需要确保自身高可用
    • 客户端代理:在连接池或数据库驱动上进行一层封装,内部与不同的数据库建立连接,并对SQL进行必要的操作,比如读写分离选择走主库还是从库,分库分表select后如何聚合结果。优点是实现简单,天然去中心化,缺点是支持语言较少,版本升级困难

    一些常见的数据库中间件如下:

    • Cobar:阿里开源的关系型数据库分布式服务中间件,已停更
    • DRDS:脱胎于Cobar,全称分布式关系型数据库服务
    • MyCat:开源数据库中间件,目前更新了MyCat2版本
    • AtlasQihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目,同时还有一个NoSQL的版本,叫Pika
    • tddl:阿里巴巴自主研发的分布式数据库服务
    • Sharding-JDBCShardingShpere的一个子产品,一个轻量级Java框架

    4 MySQL主从复制环境准备

    看完了一些基础理论就可以进行动手了,本小节先准备好MySQL主从复制的环境,基于Docker+MySQL官方文档搭建。

    4.1 主库操作

    4.1.1 拉取镜像并创建容器运行

    docker pull mysql
    docker run -itd -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql-master mysql
    docker exec -it mysql-master /bin/bash
    

    在主库中进行更新镜像源,安装vim以及net-tools的操作:

    cd /etc/apt
    echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
    apt update && apt upgrade
    apt install vim net-tools
    

    4.1.2 修改配置文件

    vim /etc/mysql/my.cnf
    

    添加下面两行数据:

    [mysqld]
    server-id=1                # 全局唯一,取值[1,2^32-1],默认为1
    binlog-do-db=test          # 表示需要复制的是哪个库
    

    修改完成后重启。

    4.1.3 准备数据源

    create database test;
    use test;
    create table user(
    	id int primary key auto_increment,
    	name varchar(30) not null,
    	age int not null
    );
    

    4.1.4 创建一个复制操作的用户(可选但推荐)

    注意创建用户需要加上mysql_native_password,否则会导致从库一直处于连接状态:

    create user 'repl'@'172.17.0.3' identified with mysql_native_password by '123456';
    grant replication slave on *.* to 'repl'@'172.17.0.3';
    

    具体的地址请根据从库的地址修改,可以先看后面的从库配置部分。

    4.1.5 数据备份(可选)

    如果原来的主库中是有数据的,那么这部分数据需要手动同步到从库中:

    flush tables with read lock;
    

    开启主库的另一个终端,使用mysqldump导出:

    mysqldump -u root -p --all-databases --master-data > dbdump.db
    

    导出完成后,解除读锁:

    unlock tables;
    

    4.1.6 查看主库状态

    show master status;
    

    在这里插入图片描述

    需要把File以及Position记录下来,后面从库的配置需要用到。

    4.2 从库操作

    4.2.1 拉取镜像并创建容器运行

    docker pull mysql
    docker run -itd -p 3307:3306 -p 33061:33060 -e MYSQL_ROOT_PASSWORD=123456 --name mysql-slave mysql
    docker exec -it mysql-slave /bin/bash
    

    进入容器后,像主库一样更新源然后安装vimnet-tools

    cd /etc/apt
    echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
    apt update && apt upgrade
    apt install vim net-tools
    

    4.2.2 修改配置文件

    vim /etc/mysql/my.cnf
    

    添加如下两行:

    [mysqld]
    server-id=2            # 全局唯一,不能与主库相同
    replicate-do-db=test   # 与主库相同,表示对该库进行复制
    

    修改完成后重启。

    4.2.3 查看ip地址

    查看从库的ip地址,用于给主库设置同步的用户:

    ifconfig
    

    输出:

    inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
    

    那么主库中用于复制的用户就可以是repl@172.17.0.3

    4.2.4 导入数据(可选)

    如果主库有数据可以先导入到从库:

    mysqldump -u root -p --all-databases < dbdump.db
    

    4.2.5 准备数据源

    create database test;
    use test;
    create table user(
    	id int primary key auto_increment,
    	name varchar(30) not null,
    	age int not null
    );
    

    4.2.6 设置主库

    可以使用change master to/change replication source to8.0.23+)命令:

    change replication source to 
    source_host='172.17.0.2',                   # 可以使用ifconfig查看主库ip
    source_user='repl',                         # 之前主库创建的用户
    source_password='123456',                   # 密码
    source_log_file='binlog.000003',            # 之前在主库上使用show master status查看的日志文件
    source_log_pos=594;                         # 同样使用show master status查看
    

    4.2.7 开启从库

    start slave;
    show slave statusG
    

    新版本(8.0.22+)可使用:

    start replica;
    show replica statusG
    

    需要IOSQL线程显示Yes才算成功:

    在这里插入图片描述

    4.3 测试

    主库选择插入一条数据:

    insert into user values(1,"name",3);
    

    然后从库就能select到了:

    在这里插入图片描述

    5 搭建Spring Boot环境

    5.1 新建项目并引入依赖

    新建Spring Boot项目,并引入如下依赖:

    implementation 'com.baomidou:mybatis-plus-boot-starter:3.4.3.1'
    implementation 'com.baomidou:mybatis-plus-generator:3.5.0'
    implementation 'org.apache.velocity:velocity-engine-core:2.3'
    implementation 'org.realityforge.org.jetbrains.annotations:org.jetbrains.annotations:1.7.0'
    implementation 'com.alibaba:druid:1.2.6' # 注意不能使用druid的starter依赖,会出现模板找不到的问题
    implementation 'org.apache.shardingsphere:sharding-jdbc-spring-boot-starter:4.1.1'
    

    Maven版本:

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.3</version>
    </dependency>
    <dependency>
        <groupId>org.realityforge.org.jetbrains.annotations</groupId>
        <artifactId>org.jetbrains.annotations</artifactId>
        <version>1.7.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.6</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>4.1.1</version>
    </dependency>
    

    5.2 使用生成器

    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.config.*;
    
    public class MyBatisPlusGenerator {
        public static void main(String[] args) {
            DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/test","root","123456").build();
            String projectPath = System.getProperty("user.dir");
            GlobalConfig globalConfig = new GlobalConfig.Builder().outputDir(projectPath+"/src/main/java").openDir(false).build();
            PackageConfig packageConfig = new PackageConfig.Builder().moduleName("test").parent("com.example.demo").build();
            AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig);
            autoGenerator.global(globalConfig).packageInfo(packageConfig);
            autoGenerator.execute();
        }
    }
    

    直接运行main方法即可生成代码,配置请根据个人需要进行更改,更详细的配置可以参考笔者的另一篇文章

    5.3 配置文件

    spring:
      shardingsphere:
        datasource:
          names: master,slave                                   # 数据源名字
          master:
            type: com.alibaba.druid.pool.DruidDataSource        # 连接池
            url: jdbc:mysql://127.0.0.1:3306/test               # 主库地址
            username: root                                      # 主库用户名
            password: 123456                                    # 主库密码
          slave:
            type: com.alibaba.druid.pool.DruidDataSource        # 连接池
            url: jdbc:mysql://127.0.0.1:3307/test               # 从库地址
            username: root
            password: 123456
        masterslave:
          load-balance-algorithm-type: round_robin              # 负载均衡算法,
          name: ms
          master-data-source-name: master                       # 主库数据源名字
          slave-data-source-names: slave                        # 从库数据源名字
        props:
          sql:
            show: true                                          # 打印SQL
    

    关于负载均衡算法,目前只支持两种:

    在这里插入图片描述

    5.4 准备Controller

    @RestController
    @RequestMapping("/test/user")
    @AllArgsConstructor(onConstructor = @__(@Autowired))
    public class UserController {
        private final UserServiceImpl userService;
        @GetMapping("/write")
        public boolean write(){
            return userService.save(User.builder().age(3).name("234").build());
        }
    
        @GetMapping("/read")
        public User read(){
            return userService.getById(1);
        }
    }
    

    6 测试

    访问http://localhost:8080/test/user/write,可以看到写操作在主库进行:

    在这里插入图片描述

    访问http://localhost:8080/test/user/read,可以看到读操作在从库进行:

    在这里插入图片描述

    这样读写分离就算是可以了。

    7 参考源码

    Java版:

    Kotlin版:

    8 参考

    如果觉得文章好看,欢迎点赞。

    同时欢迎关注微信公众号:氷泠之路。

  • 相关阅读:
    dedecms likearticle 调用附加表的字段调用方式
    织梦dedecms自定义字段在首页列表页文章页的调用
    织梦系统站点首页、列表、文章页等页面点击数调用方法
    dedecms友情链接flink的调用方法
    织梦dedecms5.7后台进去就卡死解决方法
    Dede修改文章默认标题长度,让标题全显示
    dede调用时间大全标签,不同格式!
    dede调用指定栏目的标签
    python time模块的使用
    phpstudy 环境使用Composer安装thinkphp并指定版本
  • 原文地址:https://www.cnblogs.com/6b7b5fc3/p/14932216.html
Copyright © 2011-2022 走看看