一、主从复制介绍
mysqldump解决了mysql数据库的备份,它只是基于某个时间点做备份,无法解决实时备份的问题,为了解决mysql实时备份的问题,mysql官方推出了mysql主从备份机制,可以让用户通过设置mysql主从来实现数据库实时备份。
1、MySQL服务器宕机怎么办,单点故障
2、数据的安全
通过多台机器实现一主多从的方式来实现数据备份,主服务器负责让用户读写数据,从服务器负责同步主服务器数据,也可以承担用户读的任务。
至少两台机器
将主数据库的增删改查等操作记录到二进制日志文件中,从库接收主库日志文件,根据最后一次更新的起始位置,同步复制到从数据库中,使得主从数据库保持一致。
如上图所示,主从复制步骤如下:
- master服务器上的更新事件(所有DDL和DML操作)会按照顺序写入binlog中,当slave服务器连接到master服务器后,master服务器会为slave服务器开启binlog dump线程,该线程会去读取bin-log日志。
- slave服务器连接到master服务器后,slave服务器开启一个I/O线程请求master服务器的binlog dump线程,读取bin-log日志,然后将master服务器上的变更记录写入slave服务器的的relay log日志中。
- slave还会开启一个SQL线程,实时监控relay-log日志内容是否有变化。如果有变化,将其解析成SQL语句,在slave服务器上重新执行一遍。
从上面的分析,可以知道,整个MYSQL主从复制一共开启了3个线程:master服务器开启了一个 dump线程,slave服务器开启了一个 IO线程 和 一个SQL线程。
1.1.MySQL主从复制的方式
1.1.1 异步复制
MySQL主从复制默认是异步复制的。
我们上面提到的三步中,只有第一步是同步的,即主库写入binlog日志后,即可成功返回客户端,不需要等待binlog日志传递给从库。
master服务器不关心slave服务器的数据是否写入成功,因此,如果master服务器和slave服务器之间有网络延迟的话,就会造成短暂的数据不一致的现象。
如果master服务器因各种原因导致数据还没有复制过去就宕机,则会造成数据丢失问题。
但是,异步复制在三种复制方式中,效率是最高的。
1.1.2 同步复制
master服务器将变更事件发送给slave服务器后会触发一个等待,直到所有slave服务器节点返回数据复制成功的信息给master服务器。
这种复制方式最安全,但是,效率也是最差的。
1.1.3 半同步复制
master服务器将变更事件发送给slave服务器后会触发一个等待,直到其中一个slave服务器节点返回数据复制成功的信息给master服务器。
此种方式相较于异步复制,增强了数据的一致性;相较于同步复制,只需要等待一个slave服务器节点返回数据复制成功的信息给master服务器,提高了响应时间。
需要注意的是,半同步复制除了不需要master服务器等待所有slave服务器返回数据复制成功的信息之外,也不要求变更事件完全执行。因此,我们仍有可能看到在slave服务器上数据复制延迟的发生。
如果因为网络延迟等原因造成slave服务器迟迟没有返回复制成功的信息,超过了master服务器设置的超时时长,半同步复制就会降级为异步复制的方式,而后继续数据复制。
1.2 主从复制的作用
-
1.高可用性:主数据库异常可切换到从数据库
-
2.负载均衡:实现读写分离
-
3.备份:进行日常备份
1.3 实战
(一) 一主一从配置
1.服务器准备
服务器IP | 角色 | 主机名 |
---|---|---|
192.168.8.100 | Master1(主) | master01 |
192.168.8.101 | Slave1(从) | slave01 |
2.数据库安装(mysql8.0.20)
# 执行MySQL安装脚本
bash mysql_install.sh
安装完后,配置 my.cnf 1.主库配置
vim /etc/my.cnf
[mysqld]
... # 省略
# 主从复制-主机配置
# 主服务器唯一ID
server-id=1
# 启用二进制日志
log-bin=mysql-bin
# 设置不要复制的数据库(可设置多个)
binlog-ignore-db=sys
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
binlog-ignore-db=performance_schema
# 设置需要复制的数据库(可设置多个)
# binlog-do-db=test
# 设置logbin格式
binlog_format=STATEMENT
binlog_format=STATEMENT
在这种设置下,MySQL将以“语句级别”记录二进制日志,也就是记录执行的SQL语句,而不是记录实际数据行的更改。
这种设置相对较简单,在涉及到数据库复制和事务的情况下有可能数据不同步。
除了STATEMENT
格式外,MySQL还支持ROW
和MIXED
格式:
ROW
格式记录了对数据行的更改。MIXED
格式则是根据操作的类型(语句或行)来灵活选择。
2.从库配置
vi /etc/my.cnf
[mysqld]
...
# 在之前配置下方编写
# 主从复制-从机配置
# 从服务器唯一ID
server-id=2
# 启用中继日志
relay-log=mysql-relay
重启两台服务器上的mysql
/etc/init.d/mysqld restart
3.关闭主从数据库服务器防火墙或开放3306端口
# 查看防火墙状态
systemctl status firewalld
# 关闭防火墙
systemctl stop firewalld
4.主数据库创建用户slave并授权
# 在主数据库端(192.168.8.100)
# 登录mysql -uroot -p
# 创建用户
create user 'slave'@'%' identified with mysql_native_password by 'root123';
# 授权
grant replication slave on *.* to 'slave'@'%';
# 刷新权限
flush privileges;
5.从数据库端验证主数据库slave用户是否可用
# 在从数据库端(192.168.8.101)
# 验证主数据库slave用户是否可用
mysql -uslave -p -h192.168.8.100 -P3306
6.配置主从节点信息
# 在主数据库端(192.168.8.100)
# 查询服务ID及Master状态
# 登录
mysql -uroot -p
# 查询server_id是否可配置文件中一致
show variables like 'server_id';
# 若不一致,可设置临时ID(重启失效)
set global server_id = 100;
# 查询Master状态,并记录 File(对应下一步中的master_log_file)
# Position (对应下一步中的master_log_pos)的值
show master status;
# 注意:执行完此步骤后退出主数据库
# 防止再次操作导致 File 和 Position 的值发生变化
7.在从数据库端设置同步
# 在从数据库端(192.168.8.101)
# 登录
mysql -uroot -p
# 查询server_id是否可配置文件中一致
show variables like 'server_id';
# 若不一致,可设置临时ID(重启失效)
set global server_id = 101;
# 设置主数据库参数(用上一步创建的slave用户及密码)
change master to
master_host='192.168.8.100',
master_port=3306,
master_user='slave',
master_password='root123',
master_log_file='mysql-bin.000001',
master_log_pos=828;
# 开始同步
start slave;
# 若出现错误,则停止同步,重置后再次启动
stop slave;
reset slave;
start slave;
# 查询Slave状态
show slave status\G;
# 查看是否配置成功
# 查看参数 Slave_IO_Running 和 Slave_SQL_Running 是否都为yes,则证明配置成功。若为no,则需要查看对应的 Last_IO_Error 或 Last_SQL_Error 的异常值
8.测试主从复制,主服务上执行
# 在主数据库端(192.168.8.100)
mysql -uroot -p
# 创建test库,t1表,添加测试数据
create database test;
use test;
create table t1(id int,name varchar(30));
insert into t1(id,name) values(1,"aaa");
9.测试主从复制,从服务器上执行
# 在从数据库端(192.168.8.101)
mysql -uroot -p
# 查看是否同步数据
show databases;
use test;
show tables;
select * from t1;
二、GTID 方案
2.1 GTID 是什么?
GTID 的全称是 Global Transaction Identifier,全局事务 ID,当一个事务提交时,就会生成一个 GTID,相当于事务的唯一标识。
GTID 长这样:
c5d74746-d7ec-11ec-bf8f-0242ac110002:1
结构:
GTID=server_uuid:gno
server_uuid 是一个实例第一次启动时自动生成的,是一个全局唯一的值;
gno 是一个整数,初始值是 1,每次提交事务的时候分配给这个事务,并加 1。
每个 MySQL 实例都维护了一个 GTID 集合,用来对应“这个实例执行过的所有事务”。
2.2 GTID 的优势
- 1、更简单的实现 failover,不用以前那样在需要找位点(log_file 和 log_pos)。
- 2、更简单的搭建主从复制。
- 3、比传统的复制更加安全。
- 4、GTID是连续的没有空洞的,保证数据的一致性,零丢失。
2.3 如何启用 GTID
修改主库和从库的配置文件:
vim /etc/my.cnf
[mysqld]
...
## 在配置文件中添加GTID配置
gtid_mode=on
enforce_gtid_consistency=on
解释:
gtid_mode=on #GTID 是一个全局唯一的事务标识符,用于标识数据库集群中的事务。启用 GTID 后,每个事务都会被分配一个唯一的 GTID。
enforce_gtid_consistency=on #当启用 GTID 后,该选项用于强制要求从库只能复制具有与主库一致的 GTID 链的事务,以确保数据一致性。
从库配置同步的参数:
## 格式
CHANGE MASTER TO
MASTER_HOST=$host_name,
MASTER_PORT=$port,
MASTER_USER=$user_name,
MASTER_PASSWORD=$password,
master_auto_position=1;
## 例如:
change master to
master_host='192.168.8.100',
master_port=3306,
master_user='slave',
master_password='root123',
master_auto_position=1;
其中 master_auto_position 标识主从关系使用的 GTID 协议。
相比之前的配置,MASTER_LOG_FILE 和 MASTER_LOG_POS 参数已经不需要了。
2.4 GTID 同步方案
GTID 同步的原理图。
GTID 方案:主库计算主库 GTID 集合和从库 GTID 的集合的差集,主库推送差集 binlog 给从库。
当从库设置完同步参数后,主库 A 的GTID 集合记为集合 x,从库 B 的 GTID 集合记为 y。从库同步的逻辑如下:
- 从库 B 指定主库 A,基于主备协议简历连接。
- 从库 B 把集合 y 发给主库 A。
- 主库 A 计算出集合 x 和集合 y 的差集,也就是集合 x 中存在,集合 y 中不存在的 GTID 集合。比如集合 x 是 1~100,集合 y 是 1~90,那么这个差集就是 91~100。这里会判断集合 x 是不是包含有集合 y 的所有 GTID,如果不是则说明主库 A 删除了从库 B 需要的 binlog,主库 A 直接返回错误。
- 主库 A 从自己的 binlog 文件里面,找到第一个不在集合 y 中的事务 GTID,也就是找到了 91。
- 主库 A 从 GTID = 91 的事务开始,往后读 binlog 文件,按顺序取 binlog,然后发给 B。
- 从库 B 的 I/O 线程读取 binlog 文件生成 relay log,SQL 线程解析 relay log,然后执行 SQL 语句。
GTID 同步方案和位点同步的方案区别是:
- 位点同步方案是通过人工在从库上指定哪个位点,主库就发哪个位点,不做日志的完整性判断。
- 而 GTID 方案是通过主库来自动计算位点的,不需要人工去设置位点,对运维人员友好。
在线将传统复制切换到GTID复制
## 在主从库执行
set global enforce_gtid_consistency=WARN;
set global enforce_gtid_consistency=on;
set @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;
## 先在从库上执行
set @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
## 然后在主库执行
set @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
# 查看是否为0,等于0是表示所有连接都转为gtid复制
show status like 'ONGOING_ANONYMOUS_TRANSACTION_COUNT';
# 主从库执行
set global gtid_mode=on;
# 从库执行
stop slave;
change master to master_auto_position=1;
start slave;
## 永久生效,记得更改配置文件
在线将GTID的环境切换到非GTID
# 从库执行
stop slave;
change master to master_auto_position=0;
start slave;
# 主从库执行
set @@GLOBAL.GTID_MODE = ON_PERMISSIVE
# 主库执行
set @@GLOBAL.GTID_MODE = OFF_PERMISSIVE
# 这个时候已经从gtid转到传统复制了,不过还是中间状态
# 在从库执行
set @@GLOBAL.GTID_MODE = OFF_PERMISSIVE
# 主从库执行
select @@GLOBAL.GTID_OWNED;
# 是不是为空
# 主库执行
set global gtid_mode=0;
# 主库执行
set global enforce_gtid_consistency=off;
## 永久生效,记得配置文件修改