本文通过Docker快速搭建MySQL主从数据库,并用spring boot测试读写分离的实现,这是一个实验环境,便于开发人员开发业务功能代码。
在搭建环境之前保证已安装docker,本文不进行docker安装的介绍。
执行下面安装
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-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)
上面的File和Position很重要,等一下在从数据库会用到。
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 statusG;
*************************** 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上执行如下语句:
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 |
成功。
本文的读写分离用dynamic-datasource-spring-boot-starter的插件来完成的,具体的介绍可以看:
https://github.com/baomidou/dynamic-datasource-spring-boot-starter
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
特性:
使用方法:
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 切换数据源。
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;
}
}