写点什么

MySQL 数据库主从复制集群原理概念以及搭建流程

作者:jiangxl
  • 2022 年 7 月 27 日
  • 本文字数:7886 字

    阅读完需:约 26 分钟

MySQL 数据库主从复制集群原理概念以及搭建流程


1.MySQL 主从复制集群的核心概念

1.1.什么是主从复制集群

主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。


MySQL 支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。


MySQL 复制的优点主要包含以下三个方面:


  • 主库出现问题,可以快速切换到从库提供服务。

  • 实现读写分离,降低主库的访问压力。

  • 可以在从库中执行备份,以避免备份期间影响主库服务。

1.2.主从复制集群中的专业术语

在 MySQL 主从复制集群中,主要分为两大部分:文件、线程。


文件:


  • 主库

  • Binlog:二进制日志。

  • 从库

  • relay-log:中继日志,主库推送过来的 Binlog 日志都存放在中继日志中。

  • master.info:从库复制主库的信息文件,记录着主库的地址、用户、Binlog 等信息。

  • relay-log.info:中继日志的信息文件,该文件记录了上次执行 relay-log 的位置等信息。


线程:


  • 主库

  • binlog_dump_thread:二进制日志投递线程,主要将二进制日志发送给从库的线程。

  • 从库

  • IO_Thread:从库的 IO 线程,用于请求和接收主库的 Binlog 二进制日志。

  • SQL_Thread:从库的 SQL 线程,用于将主库发来的二进制日志在数据库中进行数据复制。

1.3.主从复制集群工作原理

MySQL 主从复制集群的工作原理主要围绕前面提到的文件和线程。


MySQL 主从复制集群的原理:

1)首先从库执行change master to将连接主库的配置信息记录到 master.info 文件中,此时从库上会开启两个线程:I/O 线程和 SQL 线程。

2)从库上的 I/O 线程会从 master.info 文件中读取主库的连接信息。

3)从库 I/O 线程获取到主库的信息后,会与主库进行身份认证,然后建立连接。

4)当从库 I/O 线程成功连接到主库后,主库会立即给从库分配一个 binlog_dump_thread 线程,用于推送 Binlog 日志到从库。

5)从库 I/O 线程会根据 master.info 中记录的 Binlog 信息(Binlog 日志文件、标识位号)与主库的 binlog_dump_thread 线程请求最新的 Binlog 日志。

6)这时主库的 binlog_dump_thread 线程就会去查询是否产生了新的 Binlog 日志,如果产生了新的 Binlog 日志,会截取最新的 Binlog 日志然后推送给从库的 I/O 线程。

7)从库的 I/O 线程接收到主库推送的 Binlog 日志后,会现将其存放在内存的 TCP/IP 缓存中,然后告知主库的 binlog_dump_thread 线程,Binlog 日志已收到。

8)此时从库的 I/O 线程会去更新 master.info 文件中的 Binlog 位置点信息,记录最新的 Binlog 标识号。

9)然后从库的 I/O 线程会将主库推送的 Binlog 日志写入到磁盘上的 relay-log 文件中。

10)最后由从库的 SQL 线程读取 relay-log.ifno 文件,获取 relay-log 最新的位置点,然后根据的位置点去 relay-log 中执行最新的 Binlog 日志,执行完成后会再次更新 relay-log.info 文件中记录的 relay-log 位置点。

这就是完整的主从复制工作原理。



简单来说 MySQL 主从复制的原理就是,从库的 I/O 线程读取连接主库的配置信息,然后去连接主库开始主从同步,当 I/O 线程连接上主库后,主库会立即给 I/O 线程分配一个 Dump 线程,用于推送 Binlog 日志到从库,此时 I/O 线程会根据 master.info 文件中记录的 Binlog 信息,向主库的 Dump 线程请求最新的 BInlog,Dump 线程查询到有最新的 Binlog 产生,会将最新的 Binlog 截取,然后推送给从库的 I/O 线程,I/O 线程收到 Binlog 日志后,将其存放在内存的 TCP/IP 缓存中,然后更新 master.info 文件中最新的 Binlog 信息,紧接着将 Binlog 日志写入到 relay-log 中,最后由从库的 SQL 线程从 relay-log.info 中读取 relay-log 的位置号,然后执行 relay-log 中最新的 Binlog 日志,执行完成后,再次更新 relay-log.info 中的 relay-log 位置号,以便于下次再 relay-log 中读取最新的 Binlog 日志。


relay-log 日志会通过 MySQL 中的其他线程定期清理。

1.4.主从复制中的小细节

从上面说的主从复制原理来看,好像每次都是从库向主库去请求新数据,那么什么时候从库才应该向主库请求呢?请求的频率如何?


其实是这样的:当主库上产生了新的事务,更新到 Binlog 日之后,会给 binlog_dump_thread 线程发送一个“信号”,binlog_dump_thread 线程会与从库的 I/O 线程一直建立连接,binlog_dump_thread 线程就会通知从库的 I/O 线程有新数据产生了,这时从库的 I/O 线程就带着 master.info 中记录的最新 Binlog 标识位号,向 binlog_dump_thread 线程请求最新的 Binlog,然后完成数据同步。


MySQL 主从复制第一次复制时,是按照上面说到的 10 步完成的,第二次复制时,只需要等待主库的 binlog_dump_thread 线程向从库的 I/O 线程发送信号,然后 I/O 线程去请求最新的 Binlog,最后由 SQL 线程复制数据即可。

1.5.搭建主从复制集群的前提条件

搭建 MySQL 主从复制集群的前提条件如下:


  • 首先需要准备多个 MySQL 实例,最少两个 MySQL 实例,能够实现一主一从的架构,可以在多个服务器中分布部署独立的 MySQL 节点,也可以在一台服务器中部署多个 MySQL 实例。

  • 每个 MySQL 实例都需要由单独的 sever_id。

  • 身为主库角色的 MySQL 实例需要开启二进制日志,从库在特定场景下需要开启。

  • 主库需要授权一个专门的用户作为主从复制的用户。

  • 如果主库是一个运行很多年的数据库,突然要升级为主从复制集群,最好先将主库的数据同步一份到从库中。

  • 开启专用的复制线程。

1.6.MySQL 主从复制集群的架构信息

本次主从复制的架构是一主两从的结构。


2.搭建 MySQL 多实例环境

再搭建 MySQL 主从复制集群之前,首先搭建出多个 MySQL 实例,由于服务器有限,因此通过两台机器模拟出一主两从的环境。


主从复制集群中有一个节点开启了 gtid,所有的节点都需要开启 gtid,否则主从将不能同步。

2.1.在 mysql-1 中搭建身为主库的 MySQL 实例

mysql-1 服务器中的 3306 端口的 MySQL 实例再前面已经搭建完成了,就是我们一直在使用的数据库实例,里面有数据,更加方便演示主从集群,下面只是提供一下搭建过程的步骤。


注意每个 MySQL 节点的 server_id 都要设置成不同的,如果你的一主两从都是单独在不同服务器中部署的,那么直接参考本小结的数据库实例安装即可,不用再看多实例的步骤了。


1.解压MySQL[root@mysql-1 ~]# tar xf mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz -C /usr/local/[root@mysql-1 ~]# mv /usr/local/mysql-5.7.36-linux-glibc2.12-x86_64 /usr/local/mysql
2.设置MySQL的环境变量[root@mysql-1 ~]# vim /etc/profileexport MYSQL_HOME=/usr/local/mysqlexport PATH=$MYSQL_HOME/bin:$PATHexport LD_LIBRARY_PATH=:/usr/local/mysql/lib
3.创建mysql用户[root@mysql-1 ~]# groupadd -r mysql[root@mysql-1 ~]# useradd -M -r -s /sbin/nologin -g mysql mysql
4.准备数据目录[root@mysql-1 ~]# mkdir /data/mysql[root@mysql-1 ~]# chown -R mysql. /data/mysql
5.初始化数据库[root@mysql-1 ~]# mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql
6.准备mysql配置文件[root@mysql-1 ~]# vim /etc/my.cnf[mysqld] user=mysql port=3306 server_id=1 #每个MySQL数据库的server_id都设置成不同的basedir=/usr/local/mysql datadir=/data/mysql log_bin=mysql-bingtid-mode=onenforce-gtid-consistency=truesocket=/tmp/mysql.socklog_error=/data/mysql/mysql_err.logcharacter-set-server=utf8
[mysql]socket=/tmp/mysql.sock
7.准备服务管理脚本[root@mysql-1 ~]# vim /etc/systemd/system/mysqld.service [Unit]Description=MySQL ServerDocumentation=man:mysqld(8)Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.htmlAfter=network.targetAfter=syslog.target[Install]WantedBy=multi-user.target[Service]User=mysqlGroup=mysqlExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnfLimitNOFILE = 5000
8.启动数据库[root@mysql-1 ~]# systemctl daemon-reload [root@mysql-1 ~]# systemctl start mysqld
9.设置root密码[root@mysql-1 ~]# mysqladmin -u root -P 3306 password '123456'
10.登陆数据库[root@mysql-1 ~]# mysql -uroot -p123456mysql>
复制代码

2.2.在 mysql-2 中搭建 MySQL 多实例

由于服务器数量有限,在 mysql-2 这台服务器中分别搭建两个从库。

2.2.1.安装数据库软件

1.解压MySQL[root@mysql-2 ~]# tar xf mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz -C /usr/local/[root@mysql-2 ~]# mv /usr/local/mysql-5.7.36-linux-glibc2.12-x86_64 /usr/local/mysql
2.设置MySQL的环境变量[root@mysql-2 ~]# vim /etc/profileexport MYSQL_HOME=/usr/local/mysqlexport PATH=$MYSQL_HOME/bin:$PATHexport LD_LIBRARY_PATH=:/usr/local/mysql/lib
3.创建mysql用户[root@mysql-2 ~]# groupadd -r mysql[root@mysql-2 ~]# useradd -M -r -s /sbin/nologin -g mysql mysql
复制代码

2.2.1.搭建第一个 3306 从库的 MySQL 实例

搭建第一个从库实例,端口号为 3306,server_id 为 2,数据路径为/data/mysql3306。


1.创建3306从库的数据目录[root@mysql-2 ~]# mkdir /data/mysql3306[root@mysql-2 ~]# chown -R mysql.mysql /data/mysql3306/
2.准备3306从库的配置文件[root@mysql-1 ~]# vim /etc/my3306.cnf[mysqld] user=mysql port=3306 server_id=2 #每个MySQL数据库的server_id都设置成不同的basedir=/usr/local/mysql datadir=/data/mysql3306 log_bin=/data/mysql3306/mysql-bingtid-mode=onenforce-gtid-consistency=truesocket=/data/mysql3306/mysql.socklog_error=/data/mysql3306/mysql_err.logcharacter-set-server=utf8
[mysql]socket=/data/mysql3306/mysql.sock
3.初始化3306从库[root@mysql-2 ~]# mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql3306
4.准备服务管理脚本[root@mysql-1 ~]# vim /etc/systemd/system/mysqld3306.service [Unit]Description=MySQL ServerDocumentation=man:mysqld(8)Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.htmlAfter=network.targetAfter=syslog.target[Install]WantedBy=multi-user.target[Service]User=mysqlGroup=mysqlExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my3306.cnfLimitNOFILE = 5000
5.启动数据库[root@mysql-1 ~]# systemctl daemon-reload [root@mysql-1 ~]# systemctl start mysqld3306
6.设置root密码[root@mysql-2 ~]# mysqladmin -u root -P 3306 -S /data/mysql3306/mysql.sock password '123456'
7.登陆数据库[root@mysql-2 ~]# mysql -uroot -p123456 -P3306 -S /data/mysql3306/mysql.sockmysql>
复制代码

2.2.2.搭建第二个 3307 从库的 MySQL 实例

搭建第一个从库实例,端口号为 3306,server_id 为 2,数据路径为/data/mysql3306。


1.创建3307从库的数据目录[root@mysql-2 ~]# mkdir /data/mysql3307[root@mysql-2 ~]# chown -R mysql.mysql /data/mysql3307/
2.准备3307从库的配置文件[root@mysql-1 ~]# vim /etc/my3307.cnf[mysqld] user=mysql port=3307 server_id=3 #每个MySQL数据库的server_id都设置成不同的basedir=/usr/local/mysql datadir=/data/mysql3307 log_bin=/data/mysql3307/mysql-bingtid-mode=onenforce-gtid-consistency=truesocket=/data/mysql3307/mysql.socklog_error=/data/mysql3307/mysql_err.logcharacter-set-server=utf8
[mysql]socket=/data/mysql3307/mysql.sock
3.初始化3307从库[root@mysql-2 ~]# mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql3307
4.准备服务管理脚本[root@mysql-1 ~]# vim /etc/systemd/system/mysqld3307.service [Unit]Description=MySQL ServerDocumentation=man:mysqld(8)Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.htmlAfter=network.targetAfter=syslog.target[Install]WantedBy=multi-user.target[Service]User=mysqlGroup=mysqlExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my3307.cnfLimitNOFILE = 5000
5.启动数据库[root@mysql-1 ~]# systemctl daemon-reload [root@mysql-1 ~]# systemctl start mysqld3307
6.设置root密码[root@mysql-2 ~]# mysqladmin -u root -P 3307 -S /data/mysql3307/mysql.sock password '123456'
7.登陆数据库[root@mysql-2 ~]# mysql -uroot -p123456 -P3307 -S /data/mysql3307/mysql.sockmysql>
复制代码

2.3.MySQL 多个节点搭建完毕

MySQL 多个节点已经搭建完毕了,下面来查询每个实例的 server_id。


3.配置 MySQL 主从复制集群

MySQL 数据库的多个实例节点已经搭建完成了,下面将这些搭建的数据库实例配置成主从复制集群(一主两从)。



配置 MySQL 主从复制集群的大致步骤:


  • 主库开启 Binlog 日志,从库复制主库数据要通过 Binlog 进行复制。

  • 主库创建专门用作主从复制的用户。

  • 将主库数据进行备份,再从库中恢复。

  • 配置从库连接主库的复制信息。

  • 启动主从复制集群。

3.1.在主库开启 Binlog 二进制日志

主库已经开启了 Binlog 二进制日志,我们来查一下。


mysql> show variables like '%log_bin%';+---------------------------------+-----------------------------+| Variable_name                   | Value                       |+---------------------------------+-----------------------------+| log_bin                         | ON                          || log_bin_basename                | /data/mysql/mysql-bin       || log_bin_index                   | /data/mysql/mysql-bin.index || log_bin_trust_function_creators | OFF                         || log_bin_use_v1_row_events       | OFF                         || sql_log_bin                     | ON                          |+---------------------------------+-----------------------------+6 rows in set (0.01 sec)
复制代码


从库复制主库数据,都是通过 Binlog 进行传输和恢复的。

3.2.在主库上创建主从复制的用户

mysql> grant replication slave on *.* to replicas@'192.168.20.%' identified by '123456';
复制代码

3.3.将主库数据备份并在从库中进行还原

如果是运行很久的主库,要升级为主从复制集群,那么建议将主库上的数据备份还原到从库上,避免从库一次性同步很多数据,浪费性能。


[root@mysql-1 ~]# mysqldump -uroot -p123456 -A --master-data=2 -R -E --triggers --single-transaction > all_db.sql
复制代码


在 192.168.20.12 的两个从库中还原主库的备份数据。


1.将备份上传到从库的服务器上[root@mysql-1 ~]# scp -rp all_db.sql root@192.168.20.12:/root
2.3306从库还原主库备份数据[root@mysql-2 ~]# mysql -uroot -p123456 -P3306 -S /data/mysql3306/mysql.sock mysql> set sql_log_bin=0;mysql> source /root/all_db.sql;
3.3307从库还原主库备份数据[root@mysql-2 ~]# mysql -uroot -p123456 -P3307 -S /data/mysql3307/mysql.sock mysql> set sql_log_bin=0;mysql> source /root/all_db.sql;
复制代码

3.4.配置从库连接主库的复制信息

接下来我们需要配置从库连接主库的信息,包括让从库知道主库的 IP、端口号、复制的用户密码、Binlog 相关的信息。


1)获取主库 Binlog 的信息


从库是通过 Binlog 复制主库数据的,首先要获取主库 Binlog 日志的一些信息,包括主库的 Binlog 日志使用的是哪个、要从 Binlog 日志中的哪一个事件标识号处开始复制数据。


从 Binlog 的哪一个事件标识号处开始复制数据,尤为关键,因为主库可能运行的时间长了,也有很多的数据,一定要从合适的位置处开始复制数据,要不然也会产生很大的资源浪费。


我们是用主库头一天的全库备份,然后还原到从库上的,在主库的备份文件中就记录了从库应该从哪一个 Binlog 事件标识位处开始复制数据。


vim all_db.sql-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=452;
复制代码


从备份文件中得知,我们应该让从库从主库 Binlog 事件标识位号 452 处,开始复制数据。


如果主库不是运行很久的,不需要备份数据还原到从库,而是直接搭建的主从复制集群,那么直接可以从主库状态信息那里,获取 Binlog 的标识位号,在配置主从时,一定要指对 Binlog 标识位号,否则主从将会失败。


mysql> show master status;+------------------+----------+--------------+------------------+----------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                      |+------------------+----------+--------------+------------------+----------------------------------------+| mysql-bin.000001 |      452 |              |                  | 4f87bad8-fc67-11ec-be7b-005056b791aa:1 |+------------------+----------+--------------+------------------+----------------------------------------+1 row in set (0.00 sec)
复制代码


2)配置从库连接主库进行复制的参数信息


配置从库连接主库进行复制的参数有很多,不过只需要记住这一条命令:help change master to即可,在 MySQL 交互式下执行此命令可以打印出从库连接主库的所有配置参数。


1.3306从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3306 -S /data/mysql3306/mysql.sock mysql> CHANGE MASTER TO  MASTER_HOST='192.168.20.11',  MASTER_USER='replicas',  MASTER_PASSWORD='123456',  MASTER_PORT=3306,  MASTER_LOG_FILE='mysql-bin.000001',  MASTER_LOG_POS=452,  MASTER_CONNECT_RETRY=10;  1.3307从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3307 -S /data/mysql3307/mysql.sock mysql> CHANGE MASTER TO  MASTER_HOST='192.168.20.11',  MASTER_USER='replicas',  MASTER_PASSWORD='123456',  MASTER_PORT=3306,  MASTER_LOG_FILE='mysql-bin.000001',  MASTER_LOG_POS=452,  MASTER_CONNECT_RETRY=10;  
复制代码


参数解释:


3.5.启动主从复制线程

1.3306从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3306 -S /data/mysql3306/mysql.sock mysql> start slave;
1.3307从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3307 -S /data/mysql3307/mysql.sock mysql> start slave;
复制代码

4.查看主从复制集群状态

1.3306从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3306 -S /data/mysql3306/mysql.sock mysql> show slave status\G;
1.3307从库配置[root@mysql-2 ~]# mysql -uroot -p123456 -P3307 -S /data/mysql3307/mysql.sock mysql> show slave status\G;
复制代码


当每个从库的 IO 线程和 SQL 线程的状态都是 Yes,就表示主从复制集群搭建完毕了。



MySQL 主从配置过程中从库连接主库的配置写错了应该如何解决?


只要不执行 start slave,再执行一次 change master to 会覆盖上一次配置。


1.停止主从复制mysql>stop slave;
2.清空从库连接主库的信息mysql> reset slave all;
3.重新设置mysql> CHANGE MASTER TO MASTER_HOST='192.168.20.11', MASTER_USER='replicas', MASTER_PASSWORD='123456', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=452, MASTER_CONNECT_RETRY=10;
复制代码


发布于: 刚刚阅读数: 4
用户头像

jiangxl

关注

CSDN、阿里云、华为云、51CTO等博客专家 2022.04.27 加入

CSDN博客专家、51CTO专家博主、阿里云博客专家、华为云享专家、DevOps运维领域优质创作者,擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、大规模互联网WEB集群架构

评论

发布
暂无评论
MySQL数据库主从复制集群原理概念以及搭建流程_MySQL_jiangxl_InfoQ写作社区