1. 首页
  2. >
  3. 技术专题
  4. >
  5. SpringBoot

搭建mysql主从并用springboot读写分离

本文通过docker快速搭建mysql主从数据库,并用spring boot测试读写分离的实现,这是一个实验环境,便于开发人员开发业务功能代码。

一、用docker进行msyql主从环境搭建

在搭建环境之前保证已安装docker,本文不进行docker安装的介绍。

1、安装master结点

执行下面安装

docker run --name mysql-master --privileged=true -v /home/yaokangjun/apps/docker/master-data:/var/lib/mysql -p 23306:3306 -e MYSQL_ROOT_PASSWORD=root -d xiaochunping/mysql-master

命令介绍

--name指定运行之后的容器的名称为mysql-master; --privileged指定了当前容器是否真正的具有root权限,所谓的root权限是指具有宿主机的root权限,而不仅仅只是在容器内部有root权限; -v指定了容器中指定目录挂载到宿主机上的某个目录,这样做的目的在于防止容器中配置的数据丢失,因为docker容器在重启之后是不会保留前一次在其内部运行的相关数据的,把例子的目录改成你自己的:/home/yaokangjun/apps/docker/master-data; -p表示宿主机上的某个端口映射到docker容器内的某个端口,这里也就是将宿主机的3306端口映射到容器内部的3306端口; -e表示指定当前容器运行的环境变量,该变量一般在容器内部程序的配置文件中使用,而在外部运行容器指定该参数。这里的MYSQL_ROOT_PASSWORD表示容器内部的MySQL的启动密码; -d参数指定了当前容器是在后台运行。

安装完成后,执行:

docker ps

可以看到:

-bash-4.2$ sudo docker ps CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                                            NAMES da8ccfefbce6        xiaochunping/mysql-slave       "docker-entrypoint.s…"   4 hours ago         Up 4 hours          0.0.0.0:23307->3306/tcp                          mysql-slave c3b94d07a86a        xiaochunping/mysql-master      "docker-entrypoint.s…"   4 hours ago         Up 4 hours          0.0.0.0:23306->3306/tcp                          mysql-master

如下图

搭建mysql主从并用springboot读写分离-含源码

通过容器名称mysql-master方式进入容器

docker exec -it mysql-master /bin/bash

进入容器后登录mysql:

mysql -uroot -proot

我们要为从服务器创建一个可以用来master服务器同步数据的账户,也就是创建一个专门用来复制binlog的账号,并且赋予该账号复制权限,其命令如下:

grant replication slave on *.* to 'test'@'%' identified by '123456'; flush privileges;

这里的grant replication slave是一个命令格式,表示赋予后面的账户以复制的权限,这样slave节点就能够获取到master节点对数据的更新。上述命令中创建的账户的用户名为test,密码为123456。接着我们需要查看master节点的binlog状态:

mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000003 |      589 |              |                  |                   | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)


搭建mysql主从并用springboot读写分离-含源码

上面的File和Position很重要,等一下在从数据库会用到。


2、安装slave结点

slave的过程和master相似。

执行下面安装:

docker run --name mysql-slave --privileged=true -v /home/yaokangjun/apps/docker/slave-data:/var/lib/mysql -p 23307:3306 --link mysql-master:master -e MYSQL_ROOT_PASSWORD=root -d xiaochunping/mysql-slave 
从容器启动与主容器启动参数主要有一下两点: • 所映射的宿主机的端口号不能与master容器相同,因为其已经被master容器占用; • 必须加上--link参数,其后指定了当前容器所要连接的容器,mysql-master表示所要连接的容器的名称,master表示为该容器起的一个别名,通俗来讲,就是slave容器通过这两个名称都可以访问到master容器。这么做的原因在于,如果master与slave不在同一个docker network中,那么这两个容器相互之间是没法访问的。注意这一点非常重要,之前本人按照网上的搭建方式搭建主从服务器一直无法成功,主要就是因为他们一直没有提到要设置这个参数。

进入容器并登录mysql:

docker exec -it mysql-slave /bin/bash mysql -uroot -proot

连接上MySQL服务器之后,我们就需要切换当前服务的状态,使其能够连接上master服务器,并且复制其数据:

change master to master_host='master', master_user='test', master_password='123456', master_port=3306, master_log_file='mysql-bin.000003', master_log_pos=589, master_connect_retry=30;

注意:如果出现如下错误:

mysql> change master to master_host='master', master_user='test', master_password='123456', master_port=3306, master_log_file='mysql-bin.000003', master_log_pos=589, master_connect_retry=30; ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.

执行如下:STOP SLAVE

mysql> STOP SLAVE

再执行一次,切换当前服务的状态,使其能够连接上master服务器的操作。

然后再开启主从复制:

start slave;

查看从服务器的连接状态:

mysql> show slave status\G; *************************** 1. row ***************************                Slave_IO_State: Waiting for master to send event                   Master_Host: master                   Master_User: test                   Master_Port: 3306                 Connect_Retry: 30               Master_Log_File: mysql-bin.000003           Read_Master_Log_Pos: 589                Relay_Log_File: da8ccfefbce6-relay-bin.000002                 Relay_Log_Pos: 320         Relay_Master_Log_File: mysql-bin.000003              Slave_IO_Running: Yes             Slave_SQL_Running: Yes               Replicate_Do_DB:            Replicate_Ignore_DB:             Replicate_Do_Table:         Replicate_Ignore_Table:        Replicate_Wild_Do_Table:    Replicate_Wild_Ignore_Table:                     Last_Errno: 0                    Last_Error:                   Skip_Counter: 0           Exec_Master_Log_Pos: 589               Relay_Log_Space: 534               Until_Condition: None                Until_Log_File:                  Until_Log_Pos: 0            Master_SSL_Allowed: No            Master_SSL_CA_File:             Master_SSL_CA_Path:                Master_SSL_Cert:              Master_SSL_Cipher:                 Master_SSL_Key:          Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No                 Last_IO_Errno: 0                 Last_IO_Error:                 Last_SQL_Errno: 0                Last_SQL_Error:    Replicate_Ignore_Server_Ids:               Master_Server_Id: 1                   Master_UUID: ea851c98-5553-11eb-a496-0242ac110003              Master_Info_File: /var/lib/mysql/master.info                     SQL_Delay: 0           SQL_Remaining_Delay: NULL       Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates            Master_Retry_Count: 86400                   Master_Bind:        Last_IO_Error_Timestamp:       Last_SQL_Error_Timestamp:                 Master_SSL_Crl:             Master_SSL_Crlpath:             Retrieved_Gtid_Set:              Executed_Gtid_Set:                  Auto_Position: 0          Replicate_Rewrite_DB:                   Channel_Name:             Master_TLS_Version:  1 row in set (0.00 sec)

主要看如下2个指标为true,即没有问题:


搭建mysql主从并用springboot读写分离-含源码

3、测试主从

在主mysql上执行如下语句:

create database test; create table tt(id int, name varchar(255)); create table ss(id int, name varchar(255)); insert into tt(id, name) value (1, 'Free'); insert into ss(id, name) value (1, 'KaKa');

从服务mysql上查看:

mysql> use test; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A  Database changed mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | ss             | | t_user         | | tt             | +----------------+ 3 rows in set (0.00 sec)  mysql> select * from ss; +------+------+ | id   | name | +------+------+ |    1 | KaKa |

成功。

二、springboot实现读写分离

本文的读写分离用dynamic-datasource-spring-boot-starter的插件来完成的,具体的介绍可以看:

https://github.com/baomidou/dynamic-datasource-spring-boot-starter

dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。

特性:

  1. 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  2. 支持数据库敏感配置信息 加密 ENC()。
  3. 支持每个数据库独立初始化表结构schema和数据库database。
  4. 支持 自定义注解 ,需要继承DS(3.2.0+)。
  5. 提供对Druid,Mybatis-Plus,P6sy,Jndi的快速集成。
  6. 简化Druid和HikariCp配置,提供 全局参数配置 。配置一次,全局通用。
  7. 提供 自定义数据源来源 方案。
  8. 提供项目启动后 动态增加移除数据源 方案。
  9. 提供Mybatis环境下的 纯读写分离 方案。
  10. 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  11. 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  12. 提供对shiro,sharding-jdbc,quartz等第三方库集成的方案,注意事项和示例。
  13. 提供 基于seata的分布式事务方案。 附:不支持原生spring事务。
  14. 提供 本地多数据源事务方案。 附:不支持原生spring事务。

使用方法:

1、引入dynamic-datasource-spring-boot-starter。

<dependency>   <groupId>com.baomidou</groupId>   <artifactId>dynamic-datasource-spring-boot-starter</artifactId>   <version>${version}</version> </dependency>

2、配置数据源。

# 服务端口 server.port=8099 # 服务名 spring.application.name=more-datasourse # 环境设置:dev、test、prod spring.profiles.active=dev # mysql数据库连接 spring.datasource.dynamic.primary=mysql spring.datasource.dynamic.datasource.mysql.username=root spring.datasource.dynamic.datasource.mysql.password=root spring.datasource.dynamic.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver spring.datasource.dynamic.datasource.mysql.url=jdbc:mysql://192.168.197.24:23306/test?useSSL=false&serverTimezone=UTC  spring.datasource.dynamic.datasource.slave_1.username=root spring.datasource.dynamic.datasource.slave_1.password=root spring.datasource.dynamic.datasource.slave_1.driver-class-name=com.mysql.jdbc.Driver spring.datasource.dynamic.datasource.slave_1.url=jdbc:mysql://192.168.197.24:23307/test?useSSL=false&serverTimezone=UTC   spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5  #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss #spring.jackson.time-zone=GMT+8

3、使用 @DS 切换数据源。


搭建mysql主从并用springboot读写分离-含源码


 import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.test.moredatasourse.Service.TtService; import com.test.moredatasourse.bean.Tt; import com.test.moredatasourse.mapper.SsMapper; import com.test.moredatasourse.mapper.TtMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  import java.util.List;  @Service @DS("slave_1") public class TtServiceImpl extends ServiceImpl<TtMapper, Tt> implements TtService {      @Autowired     private TtMapper ttMapper;      @Override     public List<Tt> info() {         List<Tt> tts = ttMapper.selectList(null);         return tts;      } }