MySQL主从架构
MySQL REPLICATION
在实际生产环境中,如果对数据库的读和写都在一个数据库服务器中操作。无论是在安全性、高可用性,还是高并发等各个方面都是完全不能满足实际需求的,因此,一般来说都是通过主从复制(master-slave)的方式来同步数据,再通过读写分离来提升数据库的并发负载能力这样的方案来进行部署与实施。
一、什么是MySQL REPLICATION(MySQL主从复制)
1、主从复制是指当master(主)库的数据发生变化的时候,变化会实时的同步到一个或多个slave(从)库。
2、默认情况下属于异步复制,无需维持长连接。
3、通过配置,可以复制所有的库或者几个库,甚至库中的一些表。
4、replication是MySQL内建的,本身自带。
二、MySQL REPLICATION的原理
简单的说就是master将数据库的更新操作写入二进制日志,slave同步这些二进制日志中的数据更新事件并写入中继日志文件中,然后读取relay日志,把二进制的日志解析成SQL语句,并执行这些SQL语句,使其与master中的数据一致。

注:
DML:SQL操作语句,update, insert,delete等数据更新操作语句。
Relay log :中继日志
三、MySQL REPLICATION的作用
1、读写分离,提供查询服务
使用主从复制,让主库负责写,从库负责读。这样,即使主库进行数据更新操作出现了锁表的情景,通过读从库也可以保证业务的正常运作。
2、实时灾备,用于故障切换。
当系统中某个节点发生故障时,可以方便的故障切换,实现高可用(HA)。例如,做数据的热备,slave作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
3、水平扩展数据库的负载能力
随着系统中业务访问量的增大,如果是单机部署数据库,就会导致I/O访问频率过高。有了主从复制,增加多个数据存储节点,将负载分布在多个从节点上,降低单机磁盘I/O访问的频率,提高单个机器的I/O性能。
四、MySQL REPLICATION支持的复制类型
Statement:即基于语句的复制,会将对数据库操作的sql语句写入到binlog中,效率比较高。
row:即基于行的复制,会将每一条数据的变化写入到binlog中。
mixed:即混合模型的复制,statement与row的混合,MySQL会根据执行的SQL语句选择日志保存方式。即交替使用行和语句、由MySQL服务器自行判断。
五、 MySQL REPLICATION如何工作
整体上来说,复制的工作过程有3个步骤:
(1) master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
(2) slave将master的binary log events拷贝到它的中继日志(relay log);
(3) slave根据中继日志中的事件,对salve数据库做相应的操作,使其与master中的数据一致。
六、MySQL REPLICATION常见方案:
1、 一主一从

2、 一主多从

一主一从和一主多从是最常见的主从架构,实施起来简单并且有效,不仅可以实现HA,而且还能读写分离,进而提升集群的并发能力。
一主多从,Master负责写操作,其他slave负责读,这种架构最大问题I/O压力集中,多台slave需要从master上同步数据,影响master的IO性能。
3、 级联复制(M-S-S)

级联复制模式下,部分slave的数据同步不连接主节点,而是连接从节点。因为如果主节点有太多的从节点,就会损耗一部分性能用于replication,那么我们可以让3~5个从节点连接主节点,其它从节点作为二级或者三级与从节点连接,这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。
例如,使用一台slave作为中继,分担Master的压力,slave中继需要开启bin-log,并配置log-slave-updates
4、 双主互备 (互为主从)
双主复制,也就是互做主从复制,每个master既是master,又是另外一台服务器的slave。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。

很多人误以为这样可以做到MySQL负载均衡,实际没什么好处,每个服务器需要做同样的同步更新,破坏了事物的隔离性和数据的一致性。
5、 多主一从(从5.7开始支持)

多主一从可以将多个MySQL数据库备份到一台存储性能比较好的服务器上。
七、 MySQL主从复制原理
MySQL主从复制涉及到三个线程,一个运行在主节点(binlog dump thread),其余两个(I/O thread, SQL thread)运行在从节点,如下图所示:

l主节点 binary log dump 线程
当从节点连接主节点时,主节点会创建一个binlog dump 线程,用于发送bin-log的内容。在读取bin-log中的操作时,此线程会对主节点上的bin-log加锁,当读取完成,在发送给从节点之前,锁会被释放。
l从节点I/O线程
当从节点上执行`START REPLICA; `命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的bin-log。I/O线程接收到主节点binlog dump 线程发来的更新之后,保存在本地relay-log中。
l从节点SQL线程
SQL线程负责读取relay log中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。
注:对于每一个主从连接,都需要三个线程来完成。当主节点有多个从节点时,主节点会为每一个当前连接的从节点建一个binary log dump 线程,而每个从节点都有自己的I/O线程,SQL线程。
主从复制的工作过程:
步骤一:主库上数据库的更新事件(update、insert、delete)被写到binlog
步骤二:从库启动之后,创建一个I/O线程,从库发起连接,连接到主库
步骤三:此时主库创建一个binlog dump thread线程,把binlog的内容发送到从库
步骤四:从库的I/O线程读取主库传过来的binlog内容并写入到relay log.
步骤五:从库还会创建一个SQL线程,从relay log里面读取内容,并执行读取到的更新事件,将更新内容写入到slave的数据库。
注:要实施复制,首先必须打开Master 端的binary log(bin-log)功能,否则无法实现。因为整个复制过程实际上就是Slave 从Master 端获取该日志然后再在自己身上完全顺序的执行日志中所记录的各种操作。
八、MySQL 主从复制模式
l异步模式(MySQL async-mode)
MySQL主从复制默认是异步的模式,异步模式如下图所示,这种模式下,master事务的提交不需要经过slave的确认,slave是否接收到master的binlog,master并不关心。slave接收到master binlog后先写relay log,最后异步地去执行relay log中的sql应用到自身。这样就会有一个问题,由于master的提交不需要确保slave relay log是否被正确接受,当slave接受master binlog失败或者relay log应用失败,master无法感知。

假设master发生宕机并且binlog还没来得及被slave接收,而切换程序将slave提升为新的master,就会出现数据不一致的情况!另外,在高并发的情况下,传统的主从复制,从节点可能会与主产生较大的延迟(当然MySQL后续版本陆续做了优化,推出了并行复制,以此降低异步复制的延迟)
l半同步模式(MySQL semi-sync)
基于传统异步存在的缺陷,MySQL在5.5版本推出半同步复制。可以说半同步复制是传统异步复制的改进,在master事务的commit之前,必须确保一个slave收到relay log并且响应给master以后,才能进行事务的commit。但是slave对于relay log的应用仍然是异步进行的,原理如下图所示:

相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,所以,半同步复制最好在低延时的网络中使用。
半同步模式不是MySQL内置的,从MySQL 5.5开始集成,需要master 和slave 安装插件开启半同步模式。
l全同步模式
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
九、部署MySQL主从同步(一主一从)
1、 环境准备:
|-----------|---------------|---------------|--------|
| 主机名 | IP | 系统版本/MySQL版本 | 角色 |
| localhost | 192.168.30.11 | RHEL9.4/8.4.0 | Master |
| Cong12 | 192.168.30.12 | RHEL9.4/8.4.0 | slave |
2、 配置时间服务器
建立时间同步环境,在主节点上搭建时间同步服务器。
配置好本地yum源(略)。
具体搭建过程参考《Centos9使用chrony服务同步时间》文档。
3、 配置主数据库服务器localhost
MySQL> create database HA;
MySQL> use HA;
MySQL> create table T1(id int,name varchar(20));
MySQL> insert into T1 values(1,'Tom1');
(1)、 创建主从复制的授权用户:
MySQL> create user [email protected] identified by '123456';
MySQL> grant replication client,replication slave on *.* to [email protected];
MySQL> flush privileges;
(2) 配置my.cnf:
root@localhost \~\]# vim /etc/my.cnf 添加一下内容 \[MySQLd
.............. //省略部分内容
log-bin=/data/MySQL/log/MySQL-bin #启用二进制日志
server-id=1 #数据库服务器ID标识
binlog-do-db=HA #可以被从服务器复制的库, 即二进制需要同步的数据库名
(3) 重启MySQL
root@localhost \~\]# systemctl restart MySQLd
(4) 查看master状态信息:
MySQL\>SHOW BINARY LOG STATUS; //显示主服务器的当前binlog文件及事件位置

(5) 查看二进制日志:

(6) 导出数据库
复制前要保证同步的数据库一致
\[root@localhost \~\]# MySQLdump -uroot -pAbcd1234 HA \>HA.sql #可以导出数据库
将导出的数据库传给从服务器
\[root@localhost \~\]# scp HA.sql [email protected]:\~
4、 配置从数据库服务器cong12
(1)两台数据库服务器MySQL版本要一致
MySQL\> show variables like '%version%';

(2)测试连接到主服务器是否成功
\[root@cong12 \~\]# MySQL -uslave -pAbcd1234 -h 192.168.30.11
MySQL\> show databases; #只有复制的权限, 是看不到其他库的。正常

(3)导入数据库,和主数据库服务器保持一致
\[root@cong12 \~\]# MySQL -uroot -pAbcd1234 -e "create database HA;"
\[root@cong12 \~\]# MySQL -uroot -pAbcd1234 HA\
.........省略部分内容
server-id=1
binlog-do-db=HA
log-bin=/data/MySQL/log/MySQL-bin-master
sync-binlog=1
binlog-format=row
参数说明:
sync-binlog:此参数表示每写缓冲多少次就同步到磁盘;sync_binlog=1表示同步写缓冲和磁盘二进制日志文件,不使用文件系统缓存,在使用innodb事务引擎时,在复制环境中,为了保证最大的可用性,都设置为"1",但会对影响io的性能。
(6)重启服务
root@localhost \~\]# systemctl restart MySQLd
(7) 查看master状态信息
MySQL\>SHOW BINARY LOG STATUS; //显示主服务器的当前binlog文件及事件位置

(8)导出主服务器HA库完整备份, 拷贝到 中继服务器 和slave服务器
\[root@localhost \~\]# MySQLdump -uroot -pAbcd1234 -B HA\>ha.sql
\[root@localhost \~\]# scp ha.sql [email protected]:\~
\[root@localhost \~\]# scp ha.sql [email protected]:\~
(9)部署SLAVE中继 cong12
(10)导入数据库ha.sql
\[root@cong12 \~\]# MySQL -uroot -pAbcd1234 \ .........省略部分内容 server-id = 3 relay-log=/data/MySQL/log/relay-log-bin relay-log-index=/data/MySQL/log/slave-relay-bin.index (19)重启MySQL root@cong13 \~\]# systemctl restart MySQLd
(20)指定cong12中继服务作为cong13的主:
\[root@cong13 \~\]# MySQL -uroot -pAbcd1234
MySQL\> STOP REPLICA;
MySQL\>CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.30.12', SOURCE_USER='repl',SOURCE_PASSWORD='123456',SOURCE_PORT=3306,GET_SOURCE_PUBLIC_KEY=1,SOURCE_LOG_FILE='MySQL-bin-slave1.000001',SOURCE_LOG_POS=448;
MySQL\> START REPLICA; ;
(21)查看从服务的状态
MySQL\> SHOW REPLICA STATUS\\G;

(22)在MASTER上插入数据测试:
(23)master插入数据
MySQL\> insert into T1 values(2,'tom2');
MySQL\> insert into T1 values(3,'tom3');
(24) 然后分别在slave中继,与slave上查看
Cong12中继查看:
MySQL\> select \* from HA.T1;

Cong13 slave查看:
MySQL\> select \* from HA.T1;

8、 排错:
1、 模拟故障1:
1.1、 问题场景
由于历史遗留问题,MySQL主从库的表结构不一致,主库的某个表tableA比从库表tableA少了一个字段
当尝试在主库上更改表结构时,这行alter语句会随着binlog同步到从库,如果从库执行这行语句时出错,主从同步线程就会自动停止,那样只能人为手动处理错误,然后再启动slave上的主从同步线程。场景大概是下面这个样子:
1.2、 先在cong13从库添加这个字段:
MySQL\> alter table T1 add age int default 0 after name;
1.3、 再在localhost主库添加这个字段:
MySQL\> alter table T1 add age int default 0 after name;
修改主库上的表结构,添加一个字段
从库会同步主库的,但是从库已经存在了这个字段
1.4、 查看slave状态
MySQL\> SHOW REPLICA STATUS\\G;

1.5、 解决方法1:
跳过错误的事物
Cong13从库上执行:
MySQL\> STOP REPLICA;
MySQL\> set global sql_slave_skip_counter=1;
MySQL\> START REPLICA; ;
MySQL\> SHOW REPLICA STATUS\\G; #恢复正常

1.6、 解决方法2:
slave比较少的时候还可以,但是当从库有几十台时,逐台去处理既费时又容易出错,怎样在主库这一侧一劳永逸地避免呢?
那很简单,我们不要让主库将alter语句记录到binlog中就行
我们直接这主库中关闭binlog记录
MySQL\> set sql_log_bin=off;
然后我们再执行alter语句
MySQL\> alter table T1 add age int default 0 after name;
再开启bin-log
MySQL\> set sql_log_bin=on;
2、 错误2:
主从的binlog日志文件对不上
2.1、 查看从的状态
MySQL\> SHOW REPLICA STATUS\\G;

2.2、 到它的主上查看状态

Slave上不对应
2.3、 Slave上操作:
MySQL\> STOP REPLICA;
MySQL\>CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.30.12', SOURCE_USER='repl',SOURCE_PASSWORD='123456',SOURCE_PORT=3306,GET_SOURCE_PUBLIC_KEY=1,SOURCE_LOG_FILE='MySQL-bin-slave1.000002',SOURCE_LOG_POS=415;
MySQL\> START REPLICA;
查看slave的binlog
MySQL\> SHOW REPLICA STATUS\\G;

附加知识点:
MySQL主从复制存在的问题:
l主库宕机后,数据可能丢失
l从库只有一个sql Thread,主库写压力大,复制很可能延时
解决方法:
l半同步复制---解决数据丢失的问题
l并行复制---解决从库复制延迟的问题
MySQL并行复制:
设置:
set global slave_parallel_workers=10; //设置sql线程数为10