掌握容器之力:MySQL主从复制

前言:

在业务场景中,使用MySQL主从复制(Master-Slave Replication)有许多重要的原因和优势:

  1. 负载均衡:主从复制可以将读操作分发到多个从服务器,从而减轻主服务器的负载。这有助于提高系统的性能和可扩展性,特别是在高流量的业务场景下。
  2. 高可用性:如果主服务器发生故障,从服务器可以快速接管,维护系统的可用性。这种故障切换可以通过监控工具和自动化脚本来实现,以降低系统宕机的风险。
  3. 数据备份:从服务器可以用于备份数据库。你可以在从服务器上执行备份操作,而不会影响主服务器的性能。这使得备份过程更加灵活和可控。
  4. 数据分析:从服务器可以用于执行复杂的数据分析和报告生成操作,而不会干扰主服务器上的事务处理。这可以改善业务智能和数据挖掘方面的工作流程。
  5. 读写分离:主从复制使得可以将读操作路由到从服务器,而将写操作发送到主服务器。这对于具有读写分离需求的应用程序非常有用,因为它可以提高读取性能,同时保持数据的一致性。
  6. 灾难恢复:如果主服务器发生严重故障或数据损坏,可以使用从服务器来进行灾难恢复。这可以帮助企业在灾难发生时快速恢复正常运营。
  7. 数据分发:主从复制还可以用于将数据复制到不同的地理位置或数据中心,以提高数据的可用性和地理分布。

使用Docker搭建MySQL主从复制具有以下一些优势:

  1. 环境隔离:Docker容器提供了隔离的运行环境,这意味着你可以在同一物理主机上运行多个MySQL实例,每个实例都具有独立的文件系统、网络和资源。这有助于防止不同MySQL实例之间的干扰和冲突。
  2. 快速部署:Docker容器可以快速创建和销毁,因此你可以轻松地部署MySQL主从复制环境,而无需复杂的安装和配置步骤。这对于开发、测试和部署过程非常有用。
  3. 可移植性:Docker容器可以在不同的环境中轻松迁移,因为它们包含了应用程序及其依赖的所有内容。这使得在开发、测试和生产环境之间进行无缝切换变得更加容易。
  4. 版本控制:你可以使用Docker映像标签来管理不同版本的MySQL容器。这使得可以轻松地在不同版本之间切换,以适应应用程序的需求。
  5. 资源管理:Docker容器可以限制使用的CPU、内存和其他资源,因此你可以更好地控制MySQL实例的资源消耗。这对于避免资源冲突和提高性能很有帮助。
  6. 易于升级和维护:更新和维护Docker容器非常方便。你可以通过构建新的容器映像,然后将它们替换掉旧的容器来进行MySQL版本升级或配置更改。
  7. 生态系统支持:Docker拥有庞大的生态系统,有许多现成的Docker映像和容器编排工具,如Docker Compose和Kubernetes,可以帮助你更轻松地管理和扩展MySQL主从复制环境。

一 Mysql主从复制原理

  • 1、master服务器将数据的改变都记录到二进制binlog日志中,只要master上的数据发生改变,则将其改变写入二进制日志
  • 2、salve服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/O Thread请求master二进制事件
  • 3、同时主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中
  • 4、从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致
  • 5、最后I/O Thread和SQL Thread将进入睡眠状态,等待下一次被唤醒。
  • 需要理解:
    • 1)从库会生成两个线程,一个I/O线程,一个SQL线程;
    • 2)I/O线程会去请求主库的binlog,并将得到的binlog写到本地的relay-log(中继日志)文件中;
    • 3)主库会生成一个log dump线程,用来给从库I/O线程传binlog;
    • 4)SQL线程,会读取relay log文件中的日志,并解析成sql语句逐一执行;

二 Mysql复制流程图

  • 1、master将操作语句记录到binlog日志中

  • 2、salve服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变

  • 3、salave开启两个线程:IO线程和SQL线程

    • 1)IO线程:负责读取master的binlog内容到中继日志relay log里;
    • 2)SQL线程:负责从relay log日志里读出binlog内容,并更新到slave的数据库里(保证数据一致)

    img

三 MySQL同步延迟

  • 1、造成mysql同步延迟常见原因
    • 1)网络:如主机或者从机的带宽打满、主从之间网络延迟很大,导致主上的binlog没有全量传输到从机,造成延迟。
    • 2)机器性能:从机使用了烂机器?比如主机使用SSD而从机还是使用的SATA。
    • 3)从机高负载:有很多业务会在从机上做统计,把从机服务器搞成高负载,从而造成从机延迟很大的情况
    • 4)大事务:比如在RBR模式下,执行带有大量的delete操作,这种通过查看processlist相关信息以及使用mysqlbinlog查看binlog中的SQL就能快速进行确认
    • 5)锁: 锁冲突问题也可能导致从机的SQL线程执行慢,比如从机上有一些select …. for update的SQL,或者使用了MyISAM引擎等。
  • 2、硬件方面(优化)
    • 1.采用好服务器,比如4u比2u性能明显好,2u比1u性能明显好。
    • 2.存储用ssd或者盘阵或者san,提升随机写的性能。
    • 3.主从间保证处在同一个交换机下面,并且是万兆环境。
    • 总结:硬件强劲,延迟自然会变小。一句话,缩小延迟的解决方案就是花钱和花时间。

四 实机操作

1.拉取MySQL的镜像

这里拉取的mysql镜像的版本是8.0.34

docker pull mysql:8.0.34

2.新建MySQL主服务器的容器实例,端口为3307

创建目录

mkdir -p {mysql-master,mysql-slave}/{log,data,conf,mysql-files}

启动容器

docker run -p 3307:3306 --name mysql-master \
-v $MyFilePath/Docker/mysql/mysql-master/log:/var/log/mysql \
-v $MyFilePath/Docker/mysql/mysql-master/data:/var/lib/mysql \
-v $MyFilePath/Docker/mysql/mysql-master/conf/conf.d:/etc/mysql/conf.d \
-v $MyFilePath/Docker/mysql/mysql-master/mysql-files:/var/lib/mysql-files \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:8.0.34

这里$MyFilePath为我自己的环境变量

命令解读

docker run :创建并运行一个容器

  • –name : 给容器起一个名字,比如叫做abc
  • -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
  • -d:后台运行容器
  • -e:环境变量,如密码什么的
  • -v:挂载一个数据卷到某个容器内目录,上面分别配置了日志、数据、配置的数据卷

docker使用mysql8镜像时加载指定的外部配置文件和存储目录时,也需要指定 /var/lib/mysql-files的外部目录,不然会报如下的错:

mysqld: Error on realpath() on '/var/lib/mysql-files' (Error 2 - No such file or directory)

所以在启动容器时 需要加上-v /mydata/mysql/mysql-files:/var/lib/mysql-files/

如果启动失败可以查看docker日志

docker logs -f [name]

3.进入$MyFilePath/Docker/mysql/mysql-master/conf/conf.d目录下新建my.cnf

cd $MyFilePath/Docker/mysql/mysql-master/conf/conf.d
vim my.cnf

里面编写如下的配置(建议复制):

[mysqld]

## 设置server_id,同一局域网中需要唯一

server_id=101 

## 指定不需要同步的数据库名称

binlog-ignore-db=mysql  

## 开启二进制日志功能

log-bin=mall-mysql-bin  

## 设置二进制日志使用内存大小(事务)

binlog_cache_size=1M  

## 设置使用的二进制日志格式(mixed,statement,row)

binlog_format=mixed  

## 二进制日志过期清理时间。默认值为0,表示不自动清理。

expire_logs_days=7  

## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。

## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致

slave_skip_errors=1062

4.修改完配置后重启master实例

docker restart mysql-master

5.进入mysql-master容器

docker exec -it mysql-master /bin/bash
mysql -uroot -p123456

6.在mysql-master容器实例内创建数据数据同步用户

新建一个数据同步用户:

CREATE USER 'slave'@'%' IDENTIFIED BY '123456';

给这个用户授予权限:

GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'slave'@'%';

考虑到我的MySQL8 ,密码密码必须修改为mysql_native_password的plugin才能被连接,不然无法连接。

ALTER USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

到这一步结束后,主机3307就告一段落了。

7.新建从服务器从服务器实例3308

docker run -p 3308:3306 --name mysql-slave \
-v $MyFilePath/Docker/mysql/mysql-slave/log:/var/log/mysql \
-v $MyFilePath/Docker/mysql/mysql-slave/data:/var/lib/mysql \
-v $MyFilePath/Docker/mysql/mysql-slave/conf/conf.d:/etc/mysql/conf.d \
-v $MyFilePath/Docker/mysql/mysql-slave/mysql-files:/var/lib/mysql-files \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:8.0.34

8.进入$MyFilePath/Docker/mysql/mysql-slave/conf/conf.d目录下新建my.cnf

cd $MyFilePath/Docker/mysql/mysql-master/conf/conf.d
vim my.cnf

编写如下的配置:

[mysqld]

## 设置server_id,同一局域网中需要唯一

server_id=102

## 指定不需要同步的数据库名称

binlog-ignore-db=mysql  

## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用

log-bin=mall-mysql-slave1-bin  

## 设置二进制日志使用内存大小(事务)

binlog_cache_size=1M  

## 设置使用的二进制日志格式(mixed,statement,row)

binlog_format=mixed  

## 二进制日志过期清理时间。默认值为0,表示不自动清理。

expire_logs_days=7  

## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。

## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致

slave_skip_errors=1062  

## relay_log配置中继日志

relay_log=mall-mysql-relay-bin  

## log_slave_updates表示slave将复制事件写进自己的二进制日志

log_slave_updates=1  

## slave设置为只读(具有super权限的用户除外)

read_only=1

9.修改完配置后重启slave实例

docker restart mysql-slave

10.在主数据库中查看主从同步状态

docker exec -it mysql-master /bin/bash
mysql -uroot -p123456

查看主从状态

show master status;
image-20230913225439232

11.进入mysql-slave容器

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

12.在从数据库里面配置主从复制

change master to master_host='宿主机ip', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=156, master_connect_retry=30;

主从复制命令参数说明:

  • master_host: 主数据库的IP地址;
  • master_port:主数据库的运行端口;
  • master_user:在主数据库创建的用于同步数据的用户账号;
  • master_password:在主数据库创建的用于同步数据的用户密码;
  • master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
  • master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
  • master_connect_retry:连接失败重试的时间间隔,单位为秒。

在从数据库中查看主从同步状态:

show slave status \G
image-20230913225737922

12.在从数据库中开启主从同步

start slave;

查看从数据库状态是否开启同步:

show slave status \G
image-20230913225709981

13.主从复制测试

主机新建数据库,然后新建数据表,插入数据,然后在从数据库里面查看是否同步。

本案例主数据库可以读写数据,从数据库可以同步读取主数据库的数据,从数据库写的数据只存在从数据库中,不会进行同步。

2023-09-13 22.46.42