基于 Docker 的 MySQL 主从复制搭建及原理

176 阅读7分钟

环境

Mac Intel-64

MySQL:5.7

如果是Mac M1/M2处理器,查询mysql镜像 docker pull --platform linux/x86_64 mysql:5.7

安装Docker

参见菜鸟教程 www.runoob.com/docker/maco…

如果已安装请跳过

搭建Mysql集群

拉取镜像

#拉取镜像
docker pull mysql:5.7
#查看镜像
docker images

看到如下镜像列表

image.png

启动节点

如果要使用文件挂载,注意事项参见Docker启动mysql

使用不挂载文件的方式启动:

#主节点对外映射的端口是3310,root账户密码是root
docker run -p 3310:3306 --name master -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
#从节点1对外映射的端口是3311,root账户密码是root
docker run -p 3311:3306 --name slave1 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
#从节点2对外映射的端口是3312,root账户密码是root
docker run -p 3312:3306 --name slave2 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
#从节点3对外映射的端口是3313,root账户密码是root
docker run -p 3310:3306 --name slave3 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7

可以使用docker logs containerName/containerId(比如,master)查看日志

image.png 确认容器是否启动成功。

-p表示接口映射,比如这里将master容器的3306端口映射给宿主机(就是你的电脑)的3310(也可以是其他端口)端口;

-e表示设置环境变量,比如这里是将master容器的root账号密码设置为root(也可以是其他的,自己能记住就行);

--name表示给容器命名;

-d表示让容器后台运行。

使用docker ps查看运行中的容器:

image.png

测试连接

可以使用cmd/navicat(当然也可以是别的,哈哈),使用映射的宿主机端口号,比如上面的3310等,默认root用户,密码就是上面节点启动配置的123456

image.png

配置主从

挂载文件方式

直接在宿主机挂载的~/docker/mysql/slave4/conf/(目录名自己随便取)目录下编辑my.cnf

非挂载文件方式

进入master容器

docker exec -it master /bin/bash

进入etc目录下,看到下面的my.cnf文件:

image.png 使用vim编辑my.cnf文件。(如果提示找不到vim命令,参见解决docker里面显示bash: vim: command not found

修改主从配置

使用vim打开my.cnf文件后,

image.png 进入编辑模式,在[mysqld]下添加如下内容:

对于master节点

log-bin=mysql-bin #名称没限制,一般都这么叫
server-id=1 #数字是多少没限制,但是从1开始是习惯吧

对于slave节点

log-bin=mysql-bin #名称没限制,见名知意
relay-log=mysql-relay #名称没限制,见名知意
server-id=2 #数字是多少没限制,最好连续吧,不重复就行,几个slave依次2,3,4

保存并退出,然后重启容器

docker restart master salve1 slave2 slave3

查看是否启动成功

docker ps

未启动成功的使用docker logs containerName(比如master)查看日志。

搭建主从复制+读写分离

主从复制(宿主机操作)

master节点设置

登录master数据库

mysql -uroot -P3310 -h127.0.0.1 -p #回车后输入密码,然后再次回车

image.png 然后创建账户并授权

-- 表示任意主机都可以使用账号密码slave/123456对master进行主从复制。
GRANT REPLICATION SLAVE,SELECT ON *.* TO 'slave'@'%' IDENTIFIED BY '123456';
-- 刷新授权
FLUSH PRIVILEGES;

对于上面的授权,如果只授予REPLICATION SLAVE,使用slave账户登录slave数据库节点后,会发现只能看到information_schemaimage.png 所以需要加上SELECT权限,特别是要做读写分离时

image.png 对mysql授权不太了解的参见老板:让你添加一个mysql用户并给予权限这么费劲吗?

查看master状态

show master status;-- 查看master状态

image.png Binlog_Do_DB:需要复制的数据库。

Binlog_Ignore_DB:不需要复制的数据库。

注意:File 和 Position 字段的值后面将会用到,在后面的操作完成之前,需要保证 Master 库不能做任何操作,否则将会引起状态变化,File 和 Position 字段的值变化。

slave节点设置

进入slave节点

mysql -uroot -P3311 -h127.0.0.1 -p #回车后输入密码,然后再次回车

image.png 查看slave节点状态 image.png 正常情况下,SlaveIORunningSlaveSQLRunning 都是 No,因为我们还没有开启主从复制过程。

在slave数据库的终端中,将slave配置为master的从节点

CHANGE MASTER TO master_host = '172.17.0.2',-- 容器的ip
master_user = 'slave',-- 上文创建的账号名称
master_password = '123456',-- 上文创建的账号密码
master_port = 3306, -- 容器的端口号,默认3306
master_log_file = 'mysql-bin.000028',-- 修改成上文中的File字段的值
master_log_pos = 5254, -- 修改成上文中的Position字段的值
master_connect_retry = 30;

image.png 注意事项:如果执行上面的命令出现错误,先重置。执行完下面两条命令。

stop slave; -- 停止从库复制
reset master; -- 重置master

启动从服务器复制功能

start slave;

image.png

命令说明:

master_host :Master 的地址,指的是容器的独立 ip, 可以通过 docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称 | 容器 id 查询容器的 ip image.png

master_port:Master 的端口号,指的是容器的端口号

master_user:用于数据同步的用户

master_password:用于同步的用户的密码

master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值(mysql-bin.000028)

master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值(5254)

master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是 60 秒

再次查看slave节点状态 image.png SlaveIORunningSlaveSQLRunning 都是 Yes,说明主从复制已经开启。此时可以测试数据同步是否成功。

主从复制排错:

使用 start slave 开启主从复制过程后,如果 SlaveIORunning 一直是 Connecting,则说明主从复制一直处于连接状态,这种情况一般是下面几种原因造成的,我们可以根据 Last_IO_Error 提示予以排除。 image.png

  • 网络不通
  • 检查 ip, 端口
  • 密码不对
  • 检查是否创建用于同步的用户和用户密码是否正确
  • pos 不对
  • 检查 Master 的 Position

测试主从复制

我们在master创建一个数据库

create database test;

image.png 查看slave节点 image.png

重置主从配置

如何停止从服务复制功能

stop slave;

如何重新配置主从 (即清除之前的主从配置)

stop slave;
reset master;

原理简述

relay log 很多方面都跟 binary log 差不多。区别是:从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后 SQL 线程会读取 relay-log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。

image.png

完整的主备流程图

最后让我们来看一下,一个 update 语句在节点 A 执行,然后同步到节点 B 的完整流程图。 image.png

可以看到:主库接收到客户端的更新请求后,执行内部事务的更新逻辑,同时写入 binlog。 备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。 一个事务日志同步的完整过程是这样的:

1、在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码、以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。

2、在备库 B 上执行 start slave 命令,这时侯备库会启动两个线程,io_threadsql_thread。其中, io_thread 负责与主库建立连接。

3、主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B。

4、备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)

5、sql_thread 读取中转日志,解析日志里的命令,并执行。