使用Docker搭建MySQL主从复制和主主复制
Mysql 复制架构
数据拓展
- 热备份:数据库在运行的过程中,对数据进行备份操作。相对的,还有冷备份,冷备份需要停机,然后对数据进行备份操作。
- 多活:所谓的多活,就是让数据库机器节点会存在多个,避免单点情况的出现。
- 故障切换:当一台数据库物理机出现异常状况时,可以自动的切换到其他物理机上。
- 读写分离:当存在存在多台数据库物理机,将读写操作分别交给不同的机器完成。
- 负载均衡:假设当存在多台数据库物理机接收读请求时,多个请求会均匀的分配到不同的机器上,避免大量请求压在某一台机器上。
常见架构
没有百分百的完美架构,只有适合的架构
理解 mysql 的分库分表,先了解 mysql 的架构设计
在 mysql 架构中,经常会使用到的就是读写分离,此设计理念的基础上常见架构有
一主一从或多从: 一个 mysql 数据库主节点,一个或者多个从节点.主节点与从节点进行数据同步
主主复制: 两个 mysql 主节点,主节点与主节点之间进行数据同步
级联复制: 类似一主多从架构,但是从节点分级同步
主主与级联复制结合: 双主节点同步,同时从节点分级同步
主从模式
主从简介
主从模式是使用的最多的 mysql 高可用架构。
存在一台 master 作为写机,一个或多个 slave 作为读机,实现读写分离。
之所以这么设计是因为在实际的情况下,读的请求量一般是远远大于写请求。架构图如下
优点:
读与写的节点分离,数据写入 master 节点后,再由 master 节点将数据复制到 slave 节点上
缺点:
- master 是单点存在的,如果要对 master 进行停机维护,无法接收写请求
- master 需要将写入数据复制到各个 slave 节点,复制是有一定的时间延迟的,因此有可能出现查询数据不一致
- 对 master 进行停机维护,需将某一个 slave 提升为新的 master 节点,选举规则需要进行自定义
- 当 slave 被提升为新的 master 后,可能会造成新的 master 节点与旧 master 的数据不一致
主从搭建
虚拟机中安装 Docker:Docker 安装
创建容器
下载 mysql 镜像
1 | docker pull docker.io/mysql:5.7 |
复制两台虚拟机出来,分开创建两个 MySQL 容器,分别充当 master 和 slave
首先是 mysqlm1 容器,即 master 节点
1 | docker run -d \ |
另一台虚拟机上的 mysqls1 容器,即 slave 节点
1 | docker run -d \ |
修改 master 配置
新增 MySQL 配置文件my.cnf
,路径为创建容器时映射的路径
对于 mysqlm1 容器,路径为/home/mysql/docker-data/m1/conf
目录内
1 | # For advice on how to change settings please see |
在 master 的 docker 容器中添加 mysql 权限,开启备份机复制,并且设置备份用户信息
进入 mysqlm1 容器
1 | docker exec -it mysqlm1 /bin/bash |
创建一个从服务,为从服务设置一个用户repluser
并添加权限
1 | GRANT REPLICATION SLAVE,FILE,REPLICATION CLIENT ON *.* TO 'repluser'@'%' IDENTIFIED BY '123456'; |
刷新权限
1 | FLUSH PRIVILEGES; |
退出容器,并重启容器
1 | exit |
查看容器的的 binlog 信息
binlog,即二进制日志,它记录了数据库上的所有改变,并以二进制的形式保存在磁盘中;
它可以用来查看数据库的变更历史、数据库增量备份和恢复、Mysql 的复制(主从数据库的复制)。
Position 代表现在的数据已经写到了 154 行,下一行是 155 行
1 | show master status; |
修改 slave 配置
新增 MySQL 配置文件my.cnf
,路径为创建容器时映射的路径
对于 mysqls1 容器,路径为/home/mysql/docker-data/s1/conf
目录内
1 | # For advice on how to change settings please see |
重启容器,接着进入到 mysqls1 容器,设置 master 信息,用于标注当前 slave 的 master 是谁
1 | docker exec -it mysqls1 /bin/bash |
格式
1 | change master to |
根据上面的信息,我输入的是
1 | change master to |
完成后,还需要开启 slave 中的IO和SQL线程,这两个线程主要用于 slave 中进行数据备份,可以先查看 slave 中这两个线程的状态
1 | show slave status\G; |
我们发现在 slave 中,这两个线程是关闭的,需要将这两个线程进行开启
1 | start slave; |
到此,mysql 主从复制就已经搭建完毕
注意:如果搭建成功后却无法同步,可以通过 reset slave 重新设置主从关系。
测试
case1: 查看主从相关信息
查看 slave 中的 binlog 是否已经开启
1 | show global variables like "%log%"; |
查看 master、slave 中的进程信息
1 | show processlist; |
master 节点信息
slave 节点信息
case2: 数据库同步测试
结论: 从服务中新增数据无法同步到主服务中去, 主从同步是单向的
主从复制原理
异步复制
Mysql 在进行复制操作时,默认是基于异步复制完成的。
- 事务提交到 master
- master 接收到应用事务提交请求后,更新内部的 binlog 日志,让 mysql 引擎执行事务操作,并返回给客户端执行结果信息。同时在 master 中会存在一个事件监听,其会一直监听着 master 中 binlog 日志文件的改变,一旦发现日志文件发生改变,触发 dump 线程
- dump 线程被触发后,通知 slave 中的 IO 线程现在有事务操作要进行同步
- slave 中 IO 线程接收到通知后,会从 slave 中relay-log.info文件中获取 slave 中的 binlog 日志文件和 pos 位置信息。接着会把这部分信息发送给 master 的 dump 线程
- master 的 dump 线程收到这些信息后,会根据 slave 发送的 binlog 日志文件和 pos 位置,将最新的 binlog 日志和 pos 位置后面的内容同步给 slave 的 IO 线程
- slave 的 IO 线程接收到这些信息后,会将这部分内容同步到 slave 中的 relay-bin 文件中
- 当 relay-bin 文件发生改变后,触发 slave 线程执行 sql 操作【异步】
- 当 slave 向 relay-bin 写入完成后,会向 master 返回一个 ACK 消息,同步成功。
对于这一系列的操作,可以发现 master 和 slave 在进行同步时是以异步的方式完成的,master 写入完 binlog 后,会马上通过引擎进行事务提交并向客户端返回响应,对于与 slave 同步的操作,则是异步完成的。
优点:
效率高
缺点:
可能出现数据不一致
半同步复制
半同步复制与异步复制的工作流程大体相似
不同点: 当 master 中的 binlog 日志写入完成后,其不会马上通过引擎进行事务提交,而会处于等待,等到 slave 同步完成向 master 返回 ACK 通知后,才会唤醒等待,继续向下执行。
等待的时长,默认为 10 秒,但该时间可以配置
尽量的避免了主从数据不一致,但造成吞吐量的降低
mysql 兜底方案: 使用半同步复制进行备份时 slave 节点挂掉了,那么当 master 等待 10 秒后,仍然会进行引擎提交,同时会将半同步复制切换为异步复制。等到 slave 节点重启后,又会自动的从异步复制切换到半同步复制。
半同步复制实现
异步复制(Asynchronous replication)
MySQL 默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果 crash 掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到 relay log 中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用。
1、分别进入两个 mysql 容器,加载 lib,主从节点都要配置,因为主从节点间会存在切换。
1 | install plugin rpl_semi_sync_master soname 'semisync_master.so'; |
master 和 slave 都查看一下插件信息
1 | show plugins; |
2、启用半同步(务必先启用从库,再启用主库)
从库(1:启用,0:禁止)
1 | set global rpl_semi_sync_slave_enabled= 1; |
主库
1 | set global rpl_semi_sync_master_enabled= 1; |
3、从库重启 IO Thread
1 | stop slave io_thread; |
4、在 master 节点上查看启动状态
1 | show global status like "%sync%"; |
在 master 节点上查询参数信息
1 | show global variables like '%sync%'; |
事实上,半同步复制并不是严格意义上的半同步复制
当半同步复制发生超时时(由 rpl_semi_sync_master_timeout 参数控制,单位是毫秒,默认为 10000,即 10s),会暂时关闭半同步复制,转而使用异步复制。当 master dump 线程发送完一个事务的所有事件之后,如果在 rpl_semi_sync_master_timeout 内,收到了从库的响应,则主从又重新恢复为半同步复制。
效果测试
- 正常的向 master 中添加数据,slave 可以进行正常数据更新
- 关闭 slave 的 IO,再次向 master 中添加数据
1 | stop slave io_thread; |
此时复制机制会由半同步复制转换为异步复制,当再次向 master 中添加数据,不会再次出现等待
- slave 中重新开启 IO Thread,异步复制会再次转换为半同步复制
1 | start slave io_thread; |
在 slave IO Thread 关闭这段时间内的数据,会同步到 slave 中,不会出现数据丢失
主主复制
简介
对于主从复制来说,其内部会存在一台 master 以及一台或多台 slave。但有一个非常明显的问题,master 是单点存在。一旦 master 宕机,则无法进行数据的写入。为了解决这个问题,可以使用主主复制架构。
在主主复制架构中,会存在两台 master,没有 slave。并且会对这两台 master 进行读写分离,两台 master 会进行相互的复制, 架构图如下:
在此架构中,两台 master 会进行双向复制,为什么这么做呢? 因为假设现在负责写的 master 宕机了,那么写的工作则会交给之前负责读的服务器来完成,相当于它即负责写又负责读。等到原先负责写的 master 恢复了,其在继续负责写工作。 反之亦然。因此才需要两者间进行双向复制。
缺点: 读请求的并发量过大,服务可能产生宕机, 主主复制架构直接使用的情况较少。
主主搭建
因为在 master 和 slave 虚拟机上,已经创建了主从模式。
所以我在两台虚拟机上再各自创建一个容器,用于测试搭建主主模式。
分别在 master 和 slave 虚拟机上创建一个名为 mysqlm2 的容器。
1 | docker run -d \ |
修改配置文件
1 | vi /home/mysql/docker-data/m2/conf/my.cnf |
分别指定不同的主机号,我指定为 131 和 132
1 | # For advice on how to change settings please see |
在主机号为 130 和 131 的容器内添加相关配置,虽然是主主模式也要添加从用户,(互为对方的从用户)
1 | docker exec -it mysqlm2 /bin/bash |
1 | #添加权限 |
从新启动两个容器。
1 | #分别在在主机号为130和131的容器上运行,查看File和Position |
查看主机号为 130 的进程列表
1 | show processlist; |
查看主机号为 131 的进程列表
级联复制(了解)
写请求的入口为一个,但当 master 向 slave 进行复制时,对于 slave 可以分为多层, master 只要向其中两台 slave 复制即可,然后再由 slave 将其数据复制到后面更多的 slave 中。通过这种方式可以减轻 master 向 slave 复制的 IO 压力。但是这种架构会使 slave 的延迟会加大,架构如下图:
双主与级联复制(了解)
对于 master 在前面几种架构设计中,都存在单点问题, 对于 master 单点问题的解决,可以采用当前的架构。
通过这种架构不仅可以解决 master 单点的问题,也可以解决 slave 延迟的问题, 架构图如下: