MySQL8.0主从复制


一、主从复制介绍

mysqldump解决了mysql数据库的备份,它只是基于某个时间点做备份,无法解决实时备份的问题,为了解决mysql实时备份的问题,mysql官方推出了mysql主从备份机制,可以让用户通过设置mysql主从来实现数据库实时备份。

1、MySQL服务器宕机怎么办,单点故障

2、数据的安全

通过多台机器实现一主多从的方式来实现数据备份,主服务器负责让用户读写数据,从服务器负责同步主服务器数据,也可以承担用户读的任务。

至少两台机器

主从

将主数据库的增删改查等操作记录到二进制日志文件中,从库接收主库日志文件,根据最后一次更新的起始位置,同步复制到从数据库中,使得主从数据库保持一致。

如上图所示,主从复制步骤如下:

  1. master服务器上的更新事件(所有DDL和DML操作)会按照顺序写入binlog中,当slave服务器连接到master服务器后,master服务器会为slave服务器开启binlog dump线程,该线程会去读取bin-log日志。
  2. slave服务器连接到master服务器后,slave服务器开启一个I/O线程请求master服务器的binlog dump线程,读取bin-log日志,然后将master服务器上的变更记录写入slave服务器的的relay log日志中。
  3. 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还支持ROWMIXED格式:

  • 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;

## 永久生效,记得配置文件修改