乐趣区

关于springboot: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

增加如下两行:

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 status\G

新版本(8.0.22+)可应用:

start replica;
show replica status\G

须要 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:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
  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版:

  • Github
  • 码云
  • CODE CHINA

Kotlin版:

  • Github
  • 码云
  • CODE CHINA

8 参考

  • MySQL 官网文档
  • 腾讯云 - 面试题: 你们有没有做 MySQL 读写拆散?如何实现 MySQL 的读写拆散?
  • 知乎 - 数据库中间件详解(精品长文)
退出移动版