Table of Contents
3.2 MySQL的异步复制 —— 基于二进制日志文件位置的主从复制
3.3 MySQL的异步复制 —— 基于全局事务标识(GTID)的主从复制
1. MySQL锁
shared lock(read lock | 共享锁):共享,相互不干扰
exclusive lock(write lock | 排它锁):写操作一旦建立,所有对数据进行读或写操作都会被阻塞
锁跟事务是联系在一起的,锁的生命周期从事务开始到事务结束(不管是提交还是回滚都是结束)
2. MySQL事务(event)
原子性(Atomicity):一个整体。要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态
一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定
隔离性(Isolation):当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰。也就是说多个并发事务之间要相互隔离。T2事务要么在T1事务开始之前结束,要么在T1事务结束之后开始
持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的
3. MySQL的复制方式
3.1 实验环境
MySQL下载地址:https://dev.mysql.com/downloads/mysql/
保留以下rpm包
node1:192.168.1.11【安装mysql】主节点
node2:192.168.1.12【安装mysql】从节点
node3:192.168.1.13
官方设置文档:https://dev.mysql.com/doc/refman/5.7/en/replication-howto-masterbaseconfig.html
3.2 MySQL的异步复制 —— 基于二进制日志文件位置的主从复制
3.2.1 主从复制原理
- 在主库上把数据更改记录到二进制日志(Binary Log)中(这些记录被称为二进制日志事件)
- 备库将主库上的日志复制到自己的中继(Relay Log)日志中
- 备库读取中继日志中的事件,将其重放到备数据库之上(从库生成两个线程,一个IO线程,一个SQL线程,IO线程去请求主库的binlog,SQL线程进行日志回放来复制)
3.2.2 配置主节点,启用复制主节点
- 修改配置文件
vim /etc/my.cnf
###
log-bin=mysql-bin
server-id=1
###
- 创建用于复制的用户
启动数据库
获取初始密码
安全初始化并设置密码
由于我们使用rpm包安装的mysql,因此这里是默认激活了密码检测插件,密码要遵循"大写字母 + 小写字母 + 数字 + 特殊字符且不能低于8位"的原则
mysql_secure_installation # 安全初始化
创建用户并授权
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'Du@961028'; # 创建用户repl在192.168.1.%网段上
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%'; # 授权
- 获取主节点上二进制名称和位置
mysql> SHOW MASTER STATUS;
3.2.3 配置从节点
- 修改配置文件
vim /etc/my.cnf
###
server-id=2
###
启动数据库
获取初始密码
安全初始化并设置密码
mysql_secure_installation # 安全初始化
之前我们配置主节点时,允许所有192.168.1网段的repl用户访问,这里我们来进行测试
- 在从节点上配置主节点信息,并开启复制
CHANGE MASTER TO # 更改从属服务器用于连接到主服务器,读取主二进制日志和读取从属中继日志的参数
MASTER_HOST='192.168.1.11', # 主节点IP地址或主机名称
MASTER_USER='repl', # 主节点用户
MASTER_PASSWORD='Du@961028', # 主节点密码
MASTER_LOG_FILE='mysql-bin.000002', # 主节点日志文件
MASTER_LOG_POS=1171; # 主节点日志位置
- 查看从节点状态
show slave status\G
Slave_IO_Running(IO线程)和Slave_SQL_Running(SQL进程)状态都为Yes,则表示复制正常
3.2.4 测试
注意:写操作只能在主节点(master)进行,因为node2会同步node1信息,而node1不会同步node2信息
- 在node1[master节点]创建数据
mysql> CREATE DATABASE dsd # 创建数据库
mysql> USE dsd # 进入数据库
mysql> CREATE TABLE dsdtable ( # 创建表
-> username varchar(10) not null,
-> password varchar(15) not null );
mysql> DESC dsdtable; # 查看表
mysql> INSERT INTO dsdtable VALUES ('user1','111'); # 表中插入数据
mysql> SELECT * FROM dsdtable; # 查看表中数据
- 在node2[slave节点]查看数据
那么,具体是怎么实现的呢?是把数据复制过去了还是怎样?
- 查看node1的日志
cd /var/lib/mysql
mysqlbinlog mysql-bin.000002
3.3 MySQL的异步复制 —— 基于全局事务标识(GTID)的主从复制
对比二进制日志位置主从复制的优点:事务原子性,要么全部成功,要么全部失败。排除了二进制主从复制因为中间插入别的编号导致废数据的情况
全局事务标识符(GTID)是创建的唯一标识符,并与主节点服务器上提交的事务关联。该标识符不仅对于它起源的主节点服务器是唯一的,而且在给定复制拓扑中的所有服务器上也是唯一的。
复制的事务保留与主节点服务器上分配给该事务的GTID相同的GTID。GTID在复制的事务开始执行之前就存在,并且即使复制的事务未写入从节点服务器上的二进制日志中或在从节点服务器上被过滤掉,GTID也会保留。
3.3.1 全局事务标识(GTID)的主从复制原理
3.3.2 全局事务标识(GTID)主从复制的配置
- 编辑配置文件
# 启动GTID,主复制节点和从复制节点都需要开启
gtid_mode=ON
enforce-gtid-consistency=ON
编辑完后,重启mysqld服务
- 配置从(slave)节点使用GTID自动定位
查看slave状态,若开启,则先关闭
修改复制方式
# 更改主节点信息
CHANGE MASTER TO
-> MASTER_HOST = '192.168.1.11',
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'Du@961028',
-> MASTER_AUTO_POSITION = 1;
# 开启复制
start slave;
# 查看复制状态
show slave status\G
可以看到,当前我们跟踪的事务和执行的事务为空
3.3.3 测试
- 在主节点(node1)插入数据
use dsd # 使用dsd数据库
insert into dsdtable values ('user2','222'); # 插入数据
- 在从节点(node2)查询复制数据
use dsd # 使用dsd数据库
select * from dsdtable; # 查询dsdtable中的数据
可以看到,从节点复制成功~
- 查看从节点复制状态
可以看到,当前跟踪事务和执行事务都已经存在数据,当前数据量为1
或者:
select * from mysql.gtid_executed;
3.4 MySQL的半同步复制
官方文档:https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.html
主库在执行完客户端提交的事务后不会立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟
正常复制:事务(t) -> 缓冲区(binlog buffer) -> dumper线程 -> slave
binlog buffer(check) -> slave(IO) -> relay log(中继日志) -> SQL线程 -> mysql
半同步复制:即
3.4.1 主库配置
- 安装半同步插件,并查看插件是否加载
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; # 安装半同步插件
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS # 查看插件加载情况
-> FROM INFORMATION_SCHEMA.PLUGINS
-> WHERE PLUGIN_NAME LIKE '%semi%';
- 查看是否启用
show global variables like '%semi%';
- 启用插件
临时启用
SET GLOBAL rpl_semi_sync_master_enabled = 1;
永久启用
[mysqld]
rpl_semi_sync_master_enabled=1
- 设置超时时长(ms)
临时设置
set global rpl_semi_sync_master_timeout=5000;
永久设置
[mysqld]
rpl_semi_sync_master_timeout=5000
- 查看是否加载以及超时时长设置是否正确
show global variables like '%semi%';
可以看到enabled和timeout设置成功~
- 查看半同步复制是否开启
3.4.2 从属库配置
- 安装半同步插件,并查看插件是否加载
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS # 查看插件加载情况
-> FROM INFORMATION_SCHEMA.PLUGINS
-> WHERE PLUGIN_NAME LIKE '%semi%';
- 启用插件
临时设置
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
永久设置
[mysqld]
rpl_semi_sync_slave_enabled=1
- 重启IO线程,否则半同步复制不会生效
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
- 查看插件启用
show global variables like '%semi%';
- 查看半同步复制是否启用
show status like 'Rpl_semi_sync_slave_status';
3.4.3 查看变量状态
- 检查系统变量(反映了如何配置半同步复制)
SHOW VARIABLES LIKE 'rpl_semi_sync%';
- 检查状态变量(可以监视半同步复制的操作)
SHOW STATUS LIKE 'Rpl_semi_sync%';
3.4.3 测试
主库插入数据
从属库查询数据
可以看到,复制成功了~
突发奇想,如果我们关闭从库会怎么样呢?
主库插入数据
我们刚刚设置的超时时间是5s,这里很明显已经超时
主库查看
插入成功~
从属库查看
插入的数据并没有复制过来。
主库查看半同步状态
从库启动slave,再次查看
数据又重新同步了
注:当半同步复制发生超时时(rpl_semi_sync_master_timeout参数,单位是毫秒,默认为10000【10s】,我设定为5s),会暂时关闭半同步复制,使用异步复制。当master dumper线程发送完一个事务的所有事件之后,如果在超时时间内,收到了从库的响应,那么主从又重新恢复为半同步复制。
3.5 MySQL的全同步复制(组复制)
~3.5 单主模式
3.5.1 恢复纯净环境
systemctl stop mysqld.service
cd /var/lib/mysql
rm -fr *
ps aux | grep mysql
确保已经没有mysql进程,实验环境纯净~
随机生成一个uuid
uuidgen
###
747f96a2-9641-11ea-9d2a-525400865f44
###
3.5.2 组复制配置
- 编辑配置文件
组复制基础框架配置
vim /etc/my.cnf
###node1
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
###
###node2
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
###
组复制设置
vim /etc/my.cnf
###node1
plugin_load_add='group_replication.so'
transaction_write_set_extraction=XXHASH64
group_replication_group_name="747f96a2-9641-11ea-9d2a-525400865f44"
group_replication_start_on_boot=off
group_replication_local_address= "192.168.1.11:33061"
group_replication_group_seeds= "192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061"
group_replication_bootstrap_group=off
group_replication_ip_whitelist='127.0.0.1,192.168.1.0/24'
group_replication_enforce_update_everywhere_checks=ON
group_replication_single_primary_mode=OFF
###
###node2
plugin_load_add='group_replication.so'
transaction_write_set_extraction=XXHASH64
group_replication_group_name="747f96a2-9641-11ea-9d2a-525400865f44"
group_replication_start_on_boot=off
group_replication_local_address= "192.168.1.12:33061"
= "192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061"
group_replication_bootstrap_group=off
group_replication_ip_whitelist='127.0.0.1,192.168.1.0/24'
group_replication_enforce_update_everywhere_checks=ON
group_replication_single_primary_mode=OFF
###
node1:
- 启动数据库(获取临时密码)
systemctl start mysqld
cat /var/log/mysqld.log | grep password
- 登录mysql,并更改密码
alter user root@localhost identified by 'Du@961028'; # 更改密码为Du@961028
- 关闭二进制文件再创建复制用户并授权,刷新授权表,最后打开二进制文件
避免接下来的操作被其他组成员复制
SET SQL_LOG_BIN=0; # 关闭二进制日志文件
CREATE USER group_rpl@'192.168.1.%' IDENTIFIED BY 'Du@961028'; # 创建用户
GRANT REPLICATION SLAVE ON *.* TO group_rpl@'192.168.1.%'; # 给该网段的授权
FLUSH PRIVILEGES; # 刷新授权表
SET SQL_LOG_BIN=1; # 打开二进制日志文件
- 配置组用户
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='Du@961028' FOR CHANNEL 'group_replication_recovery';
- 安装复制插件
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
这里告知我们已经安装过了,这是因为我们刚刚在配置文件中已经安装了该插件
检查插件是否安装
SHOW PLUGINS;
- 开启组引导(只在第一次的时执行该操作,后面其他的组成员则不需要去配置)
SET GLOBAL group_replication_bootstrap_group=ON; # 启动组复制
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF; # 关闭组复制
在关闭检查之前我们也可以验证是否已经创建该组
SELECT * FROM performance_schema.replication_group_members;
node2:
- 启动服务,获取临时密码
systemctl start mysqld.service
cat /var/log/mysqld.log | grep password
- 登陆,更改密码
alter user root@localhost identified by 'Du@961028';
- 关闭二进制文件,创建复制用户并授权,最后再打开二进制文件
SET SQL_LOG_BIN=0; # 关闭二进制日志文件
CREATE USER group_rpl@'192.168.1.%' IDENTIFIED BY 'Du@961028'; # 创建用户
GRANT REPLICATION SLAVE ON *.* TO group_rpl@'192.168.1.%'; # 给该网段的授权
FLUSH PRIVILEGES; # 刷新授权表
SET SQL_LOG_BIN=1; # 打开二进制日志文件
- 添加组复制
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='Du@961028' FOR CHANNEL 'group_replication_recovery';
如果没有加载组复制插件则需要安装
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
- 开启组复制报错
问题分析:在配置组复制之前,启动mysql时,配置文件中已经设置了binlog_format=on,初始化mysql更改密码时,有事物已经被记录,所以报错,将master重置之后,删除之前的事物,再启动组复制则没有问题,类似于主从复制中的数据不同步。
3.5.3 测试
- 在node1创建数据
- 检查表
t1
和二进制日志的内容
SELECT * FROM t1;
SHOW BINLOG EVENTS;
在node2上新建表
提示为只读模式,因为是单主复制,所以只有一个可读,其他为只读
~3.6 多主模式
在单主模式基础上,在所有组的复制节点上执行如下命令
- 停止组复制,单主模式关闭,检查更新开启
stop group_replication
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON
- 选择一个节点(我是在node1)开启组引导,开启组复制,关闭引导
- 其他节点直接打开组复制
- 查看组复制状态
- 在node2创建表
- 在node1查看
- 在node1插入数据
报错,因为刚创建的表没有主键,所以无法插入数据
组复制只支持innodb存储引擎,能够创建非innodb引擎的表,但是无法写入数据,向非innodb表写数据直接报错。
- 在node1重新创建表,插入数据
- 在server2查看
可以看到node1上我们所插入的数据