1、概述
MySQL 的主从复制又叫 Replication、AB 复制。至少需要两个 MySQL 服务(可以是同一台机器,也可以是不同机器之间进行)。
比如A服务器做主服务器,B服务器做从服务器,在A服务器上进行数据的更新,通过 binlog 日志记录同步到B服务器上,并重新执行同步过来的 binlog 数据,从而达到两台服务器数据一致。
MySQL 数据库的主从复制方案,与使用 scp/rsync 等命令进行的文件级别复制类似,都是数据的远程传输。只不过 MySQL 的主从复制是其自带的功能,无需借助第三方工具,而且MySQL的主从复制并不是数据库磁盘上的文件直接拷贝,而是通过逻辑的 binlog 日志复制到要同步的服务器本地,然后由本地的线程读取日志里面的 SQL 语句,重新应用到 MySQL 数据库中。
2、为什么需要 Mysql 主从复制
谈起为什么在大多数情况下部署 Mysql 常常使用 Mysql 主从模式进行部署这个问题,本来也去网上搜寻了一些答案,其中原因主要有以下几点:
- 做数据的热备,主库宕机后备库能够及时替换主库,保证业务可用性,能一定程度避免数据丢失。
- 实现读写分离,主库写,从库读,减小主库的读写压力。当主库执行写过程加锁时,不会堵塞从库读操作,从而提高了数据的查询效率。
- 应对业务量越来越大,I/O 访问频率过高,单机无法满足的问题。增加多个从库做负载,能够降低整体 I/O 访问频率,提高单个机器 I/O 性能。
3、数据库常用的主从复制方式
- 基于 Binlog 复制模式
MySQL 主从复制默认是异步的模式。MySQL增删改操作会全部记录在 Binlog 中,当 slave 节点连接 master 时,会主动从 master 处获取最新的 Binlog 文件。并把 Binlog 存储到本地的 relay log 中,然后去执行 relay log 的更新内容。
- GTID 复制模式
在传统的复制里面,当发生故障,需要主从切换,需要找到 Binlog 和 位点信息,恢复完成数据之后将主节点指向新的主节点。在 MySQL 5.6 里面,提供了新的数据恢复思路,只需要知道主节点的 IP、端口以及账号密码就行,因为复制是自动的,MySQL 会通过内部机制 GTID 自动找点同步。接下来我们要部署的主从也是基于这种模式。
4、GTID 概念相关介绍
GTID 是什么
GTID 指的是全局事务 ID,全程是 Global Transaction Identifier ,在整个事务流程中每一个事务 ID 是全局唯一的,且在整个主从复制架构中该 ID 都不会相同。
GTID 主从复制方式
基于 GTID 的主从复制方式的出现,主要是用于替换传统的日志点 复制方式。通过GTID 可以保证每个主库提交的事务在集群中都有 唯一 的一个事务 ID。强化了数据库主从的一致性和故障恢复数据的容错能力,在主库 宕机发生主从切换 的情况下,GTID 方式可以让其他从库自动找到新主库复制的位置。而且 GTID 可以忽略已经执行过的事务,减少了数据发生错误的概率。
GTID 的组成
GTID 由server_uuid +tid 组成,其中:
-
server_uuid:
server_uuid
是在 Mysql 首次启动过程中自动生成的一个uuid(128位)
随机值,生成后会将该值存储到数据目录的auto.cnf
中。因为是随机值,所以不同服务器的 Mysql 的server_uuid 都是不相同的。 -
tid: 代表了该实例上已经提交的事务数量,是一个整数,初始值是
1
,每次提交事务的时候分配给这个事务并加1
。
其组成样式如下:
fb90fba5-60cf-11eb-b5fa-000c295fbc5f:21
GTID 复制工作原理
假设从库开启了 binlog,那么执行流程如下:
- ① 主节点执行事务提交前会产生一个
GTID
,其会随着事务一起记录到binlog
日志中。 - ② 从节点
I/O Thread
会读取主节点的binlog
日志文件并存储在从节点的relaylog
日志中。从节点将主节点的GTID
这个值配置到gtid_next
中,即下一个要读取的 GTID 值。 - ③ 从节点读取
gtid_next
中的值,然后查找自己的binlog
日志中是否有这个GTID
。 - ④ 如果有这个记录,说明这个
GTID
的事务已经执行过了,就忽略掉。 - ⑤ 如果没有这个记录,从节点就会执行该 GTID 事务,并记录到自己的
binlog
日志中。在读取执行事务前会先检查其他session
中是否持有该GTID
,确保不被重复执行。 - ⑥ 在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
GTID 使用中的限制条件
GTID 复制是针对事务来说的,一个事务只对应一个 GTID,好多的限制就在于此。其中主要限制如下:
- 不能使用
create table table_name select * from table_name
。 - 在一个事务中既包含事务表(使用
InnoDB
存储引擎的表)的操作又包含非事务表(使用MyISAM
存储引擎的表)。 - 不支持创建或删除临时表操作,如
CREATE TEMPORARY TABLE or DROP TEMPORARY TABLE
语句操作。 - 使用 GTID 复制从库跳过错误时,不支持执行该
ql_slave_skip_counter
参数的语法。
如何开启 GTID 模式
需要在 Mysql 配置文件中添加下面几条配置:
## 开启 gtid 模式
gtid_mode=on
## 配置不允许任何事务违反 GTID 一致性,用于保证数据一致性
enforce_gtid_consistency=on
## 开启 Binlog 并指定名称(可选)
log_bin=binlog
## 从节点从主节点接收到更新且执行,是否将记录存到从节点的 binlog 日志中(可选)
log-slave-updates=on
## 当从数据库启动的时候,从节点不会启动复制(可选)
skip-slave-start=1
MySQL8.0 之前 mysql 密码加密方式为 mysql_native_password,而 MySQL8.0 版本默认新添加的用户密码默认使用的 caching_sha2_password,因此进行使用主从复制时可能会遇到错误,需要将加密方式改成 mysql_native_password。
5、基于GTID的主从复制原理** **
我个人理解,基于GTID的MySQL复制流程为:
1、Master写入记录前生成一个GTID
2、Master将GTID和数据一起写入二进制日志中
3、Master刷新日志数据到磁盘中
4、Slave读取Master的二进制日志中的GTID
5、Slave判断该GTID是否已经写入中继日志,如果写入了则说明该GTID代表的数据已经存在,跳过该条数据不将其重复录入到中继日志中
6、Slave继续往下读取Master二进制日志,直到读取到Slave中继日志中不存在的GTID即开始将其写入Slave中继日志,SQL线程启动,开始刷新数据到Slave磁盘中
7、复制过程结束
这样就可以避免Slave中漏写Master的数据或者重复写入。
6、环境准备
Linux版本:CentOS7
MySQL版本:mysql8.0
Docker版本:19.03.9
DockerCompose版本:1.27.4
有很多种配置主从同步的方法,可以总结为如下的步骤:
- 在主服务器上,必须开启二进制日志机制和配置一个独立的ID
- 在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号
- 在开始复制进程前,在主服务器上记录二进制文件的位置信息
- 如果在开始复制之前,数据库中已经有数据,就必须先创建一个数据快照(可以使用mysqldump导出数据库,或者直接复制数据文件)
- 配置从服务器要连接的主服务器的IP地址和登陆授权,二进制日志文件名和位置
主从目录结构
.
├── config
│ ├── master.cnf
├── master-data
├── .env
└── docker-compose.yml
目录/文件说明
-
config/master.cnf
: MySQL Master节点的配置文件 -
docker-compose.yml
: 构建主从节点与挂载数据目录的docker-compose配置文件 -
master-data
: 主节点数据位置,当然生产环境要挂到别的位置 -
.env
: 环境变量文件
主从数据库配置
主库配置
1、修改.env
文件
# default environment arguments for docker-compose.yml
# set master volumes dir
MASTER_DATA=./mysql-volumes/data/master
MASTER_LOGS=./mysql-volumes/logs/master
MASTER_CONF=./mysql-volumes/conf/master
# set master root password
MASTER_PASSWD=P@ssw0rd
MASTER_DATA
是 Master节点的数据目录,需要修改到宿主机对应的位置。MASTER_LOGS
是 Master节点的日志目录,需要修改到宿主机对应的位置。MASTER_CONF
是 Master节点的配置目录,需要修改到宿主机对应的位置。MASTER_PASSWD
是主节点的root密码,bin目录下的脚本会读取这个变量的值从而进行访问数据库
2、修改配置文件
cd ./mysql-volumes/conf/master
vim master.cnf
配置内容如下:
[mysqld]
max_connections = 2000
default-time_zone=‘+8:00‘
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
#gtid:
server_id = 1 #服务器id
gtid_mode = on #开启gtid模式
enforce_gtid_consistency = on #强制gtid一致性,开启后对于特定create table不被支持
#binlog
log_bin = mysql-binlog
log_slave_updates = on
binlog_format = row #强烈建议,其他格式可能造成数据不一致
#relay log
skip_slave_start = 1
default_authentication_plugin = ‘mysql_native_password‘ #更改加密方式
3、配置docker-compose.yml
version: ‘3.3‘
services:
mysql-master:
image: mysql:8.0
container_name: mysql-master
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
ports:
- 3316:3306
environment:
- MYSQL_ROOT_PASSWORD=${MASTER_PASSWD}
volumes:
- ${MASTER_CONF}:/etc/mysql/conf.d
- ${MASTER_LOGS}:/var/log/mysql
- ${MASTER_DATA}:/var/lib/mysql
4、启动主库
在docker-compose.yml目录下执行docker-compose up -d
5、在主库中创建用于主从复制的账号
# 连接mysql服务
docker exec -it mysql-master mysql -uroot -pP@ssw0rd
#查看server_id是否生效
show variables like ‘%server_id%‘;
# 查看MASTER状态
SHOW MASTER STATUS\G
# 创建主从复制账号
CREATE USER ‘replicasName‘@‘%‘ IDENTIFIED BY ‘replicasPasswd‘;
GRANT REPLICATION SLAVE ON *.* TO ‘replicasName‘@‘%‘;
flush privileges;
select user,host from mysql.user;
从库配置
1、修改.env
文件
# default environment arguments for docker-compose.yml
# set slave volumes dir
SLAVE_DATA=./mysql-volumes/data/slave
SLAVE_LOGS=./mysql-volumes/logs/slave
SLAVE_CONF=./mysql-volumes/conf/slave
# set slave root passwor
SLAVE_PASSWD=P@ssw0rd
SLAVE_DATA
是 Salve节点的数据目录,需要修改到宿主机对应的位置。SLAVE_LOGS
是 Salve节点的日志目录,需要修改到宿主机对应的位置。SLAVE_CONF
是 Salve节点的配置目录,需要修改到宿主机对应的位置。SLAVE_PASSWD
是主节点的root密码,bin目录下的脚本会读取这个变量的值从而进行访问数据库
2、修改配置文件
cd ./mysql-volumes/conf/slave
vim slave.cnf
配置内容如下:
[mysqld]
max_connections = 2000
default-time_zone = ‘+8:00‘
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
#GTID:
server_id = 2 #服务器id
gtid_mode = on #开启gtid模式
enforce_gtid_consistency = on #强制gtid一致性,开启后对于特定create table不被支持
#binlog
log_bin =mysql-binlog
log_slave_updates = on
binlog_format = row #强烈建议,其他格式可能造成数据不一致
#relay log
skip_slave_start = 1
default_authentication_plugin = ‘mysql_native_password‘ #更改加密方式
read_only = on #设置只读
3、配置docker-compose.yml
version: ‘3.3‘
services:
mysql-slave:
image: mysql:8.0
container_name: mysql-slave
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
ports:
- 3326:3306
environment:
- MYSQL_ROOT_PASSWORD=${SLAVE_PASSWD}
volumes:
- ${SLAVE_CONF}:/etc/mysql/conf.d
- ${SLAVE_LOGS}:/var/log/mysql
- ${SLAVE_DATA}:/var/lib/mysql
4、启动从库
在docker-compose.yml目录下执行docker-compose up -d
5、通过主库的账号开启主从复制
# 连接mysql
docker exec -it mysql-slave mysql -uroot -pP@ssw0rd
#查看server_id是否生效
show variables like ‘%server_id%‘;
# 查看MASTER状态
SHOW SLAVE STATUS\G
# 从节点使用备份账户连接主节点,开启备份
CHANGE MASTER TO
master_host=‘192.168.147.152‘,
master_port=3316,
master_user=‘replicasName‘,
master_password=‘replicasPasswd‘,
master_auto_position=1;
# 启动同步
RESET SLAVE;
START SLAVE;
查看SLAVE状态,都为yes表示基于GTID的MySQL主从复制已经成功!
参考:
https://blog.csdn.net/pbrlovejava/article/details/88046025
https://www.freeaihub.com/post/93490.html