由于资源有限,又觉得在一台主机上安装多个Mysql服务闲麻烦。Docker它不香吗?即省去了安装Mysql所需要的步骤,有多个容器之间相互资源独立,IP互不冲突!又随用随关,非常的方便。加上最近业务有一项有关于主从延迟引起的异常问题,于是想自己搭建一个主从来复现一下情况,顺便熟悉一下Mysql的主从复制结构。
1、拉取Docker镜像
由于用的是Mysql5.7的版本,想尽量还原一下场景,于是也使用Mysql5.7的镜像来搭建环境(未安装Docker先安装Docker)
docker pull mysql:5.7
2、按照Docker Hub上的文档启动两个Mysql容器作为主从的两个服务
# master 节点 虚拟机3306端口映射到本机的 3301端口
docker run -p 3301:3306 --name master -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# slave 节点 虚拟机3306端口映射到本机的 3302端口
docker run -p 3302:3306 --name slave -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 查看容器状态
docker ps -a
a88ca8aa5e6e mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 33060/tcp, 0.0.0.0:3302->3306/tcp slave
b71a1c597dea mysql:5.7 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 33060/tcp, 0.0.0.0:3301->3306/tcp master
容器启动以后本机可以通过Navicat等Mysql客户端工具进行连接,注意ip直接使用localhost就行,端口则使用从虚拟机映射到本地的端口也就是上面的 master: 3301和slave: 3302端口
3、进入容器修改Mysql的配置文件
主库配置
首先执行 docker exec -it master /bin/bash 进入容器,然后进入容器内Mysql的配置文件路径 /etc/mysql,并对my.cnf添加如下内容:
[mysqld]
## 同一局域网内注意要唯一
server-id=100
## 开启二进制日志功能 -- 主从复制依赖binlog
log-bin=mysql-bin
写入这些配置之后需要重启容器内的Mysql服务,执行service mysql restart,该命令会导致Docker 停止,所以还需要重新启动容器docker start 'container-name'
从库配置
从库的配置步骤和主库操作相同,只是修改Mysql的配置内容不同,从库的my.cnf添加如下内容:
## 设置server_id,需要唯一
server-id=101
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志 -- 即从主库接收到的binlog保存的地方
relay_log=edu-mysql-relay-bi
tips: 以官方5.7镜像开启的容器可能没有终端文本编辑工具,可以通过 apt install vim下载 vim 来编辑配置文件,但是使用默认源的话更新太慢,需要换国内源可以根据如下步骤
cd /etc/apt
# 备份默认源
mv source.list source.list.bck
# 换中科大源
echo deb http://mirrors.ustc.edu.cn/debian/ buster main >> source.list
echo deb-src http://mirrors.ustc.edu.cn/debian/ buster main >> source.list
# 更新软件仓库 -- 如果不换源这一步会非常吗
apt update
# 下载vim
apt install vim
主从同步的过程
备库跟主库之间维持了一个长连接。主库内部有一个线程,专门用于服务备库的这个长连接。所以主从同步的步骤如下:
1、在备库B上通过 change master 命令,设置主库A的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。
2、在备库B上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。
3、主库A校验完用户名、密码后,开始按照备库B传过来的位置,从本地读取 binlog,发给 B。
4、备库B拿到 binlog 后,写到本地文件,称为中转日志(relay log)。
5、sql_thread 读取中转日志,解析出日志里的命令,并执行。
为了避免所有操作都是通过ROOT用户执行,可以新建一个专门用于主从同步的用户。在主库上执行:
# 新建一个 slave的用户 密码为 123456
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
# 只赋予该角色 读取binlog文件以及确定服务器和主服务器位置的权限
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
4、根据3中主从同步的过程配置参数以及开启主从同步
进入从库执行change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000002', master_log_pos= 1565, master_connect_retry=30; 也就是主从同步过程1中的需要的参数
参数解读:
master_port:Master的端口号,指的是容器的端口号
master_user:用于数据同步的用户
master_password:用于同步的用户的密码
master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值, 可通过 在主库执行 show master status 查看
master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值 可通过 在主库执行 show master status 查看
master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是60秒
随后在从库执行start slave即克开启主从同步啦!!
tip: 本人在搭建过程中碰到了主从同步失败的问题:
也就是图中的Slave_IO_Running和Slave_SQL_Running不全是Yes,出现这种情况都可以通过show slave status查看相关的Error信息,出现主从同步异常应该都是从这里查看更多的有效信息。本人遇到的问题就是由于数据不一致导致同步失败(一开始主库有的表从库没有的表,然后删除主库的这个表就出现问题了),解决这个问题有两种途径:
1、查看主库的master status 在从库重新执行 change master to .... 操作
2、直接在从库执行 set global sql_slave_skip_counter =1;
这两种都是需要先停止主从同步即先执行stop slave!
至此,通过Docker,Mysql一主一从的主从服务结构就搭建完毕了!看上去搭建过程比较简单,但是涉及到的只是点感觉还是又一些,特别是需要理解主从同步的过程!理解了该过程,很多问题都不是问题。主从搭建好了之后,后续需要考虑如何复现业务问题了,继续加油!