作用:主从复制在工作中可能会有两种形式:
传统AB复制,基于binlog日志 + pos点位实现复制
MySQL5.7及以后版本新增基于GTID的主从复制
相对于传统AB复制,基于GTID的主从复制在两方面比较灵活:
g:global
T:事务
配置灵活,不需要关心binlog及点位,直接配置,自动追踪
跳过异常,也比较灵活,简单操作就可以解决主从复制中的异常信息(SQL异常)
基于全局事务标识符(GTID)复制
GT=global transaction GTID = 服务器ID + 事务ID
官网:https://dev.mysql.com/doc/refman/8.0/en/replication-gtids.html
事务标识符:每执行一次事务操作(增、删、改),系统都会给其定义一个唯一编号(很长的字符串)。
GTID:一个基于原始MySQL服务器生成的一个已经被成功执行的全局事务ID,它由服务器ID以及事务ID组合而成
全局事务ID不仅仅在原始服务器上唯一,在所有存在主从关系的MySQL服务器上也是唯一的
- 幂等执行同一个 GTID 在单台 MySQL 实例只会被执行一次,自动跳过重复事务,杜绝主从重复执行造成数据错乱。
- 复制启动方式变更 传统复制:依靠
MASTER_LOG_FILE + MASTER_LOG_POS二进制日志文件 + 位置点启动同步。GTID 复制:配置MASTER_AUTO_POSITION=1,自动获取同步起点,无需手动找日志位点。 - 从库必须开启 binlog(关键区别)
- 普通传统主从:从库只开 relay log,binlog 可关闭;
- GTID 主从:从库必须开启 binlog,用来持久化已经执行完成的 GTID 事务号,保证故障切换时事务不会重复执行。
Master(主库):必须开启 binlog
Slave(从库):必须开启 relay log + 必须开启 binlog,用于保存已执行 GTID
GTID的优势
-
更简单的实现 failover,不用以前那样在需要找位点(log_file 和 log_pos)。
-
更简单的搭建主从复制。
-
比传统的AB复制更加安全。
-
GTID 是连续的没有空洞的,保证数据的一致性,零丢失。
GTID工作原理
GTID:全局事务ID编号,server_uuid + 事务序号,不管单独MySQL服务器还是主从集群环境中,都是唯一的。
- 从库连接主库,把本机已经执行完的所有 GTID 集合(
gtid_executed)发送给主库。- 主库拿到从库的 GTID 集合,做集合运算:主库全部 GTID − 从库已执行 GTID = 待同步事务差集。
- 校验:如果主库的 binlog 已经清理掉了差集对应的事务,直接报错,防止数据缺失。
- 主库自动定位到差集里第一条事务对应的 binlog 起始位置。
- 主库从该事务开始,持续把后续 binlog 推送给从库 IO 线程。
- 从库写入 relay log;SQL 线程执行事务。
- 关键特性:同一个 GTID 只会执行一次,自动跳过重复事务,保证幂等。
举例:
- 主库已执行 GTID:1~100
- 从库已执行 GTID:1~90
- 差集:91~100主库自动找到 GTID=91 的位置,从此处开始推送 binlog。
GTID的配置与实现
作用:基于GTID实现主从复制(重点)
文档:https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-howto.html
环境说明:
关闭防火墙+SELinux、配置IP与主机映射、时间同步、安装必备包
|----------------|-------------|-----------|
| IP | 主机名 | 角色 |
| 192.168.88.101 | mysql-node1 | master(主) |
| 192.168.88.102 | mysql-node2 | slave(从) |
环境准备
① 安装必备软件,如vim、wget、rsync
② 配置IP、主机名
③ 配置IP与主机映射 => /etc/hosts
④ 优化Linux系统
⑤ 时间同步
主从服务器都要安装一些依赖包
dnf install vim wget rsync telnet net-tools -y
主从服务器都要设置一下主机名
hostnamectl set-hostname mysql-node1
hostnamectl set-hostname mysql-node2
主机映射,两个服务器都要添加
vim /etc/hosts
192.168.88.101 mysql-node1 node1
192.168.88.102 mysql-node2 node2
优化系统,一些防火墙设置
sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F
iptables -t nat -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
时间同步
dnf -y install chrony
systemctl enable chronyd --now
从服务器安装MySQL脚本
#!/bin/bash
if rpm -q libaio &> /dev/null; then
echo "libaio已安装,跳过安装"
else
echo "开始安装依赖"
dnf -y install libaio &> /dev/null
if [ $? -ne 0 ];then
echo "libaio安装失败"
exit 1
fi
fi
echo "进行解压操作"
if ls -l mysql-8.0.43-linux-glibc2.28-x86_64 &> /dev/null; then
echo "已解压,跳过"
else
if [ -f mysql-8.0.43-linux-glibc2.28-x86_64.tar.xz ]; then
tar -xf mysql-8.0.43-linux-glibc2.28-x86_64.tar.xz
ls -l mysql-8.0.43-linux-glibc2.28-x86_64
fi
fi
echo "判断是否安装过MariaDB,进行清理"
rpm -qa | grep mariadb | xargs -r dnf remove -y
if [ -f /etc/my.cnf ]; then
rm -rf /etc/my.cnf
fi
id mysql &> /dev/null
[ $? -ne 0 ] && useradd -r -s /sbin/nologin mysql
rm -rf /export/server
mkdir -p /export/server
cp -r mysql-8.0.43-linux-glibc2.28-x86_64 /export/server/mysql
chown -R mysql:mysql /export/server/mysql
echo "正在进入mysql目录,对其进行初始化操作..."
cd /export/server/mysql
bin/mysqld --initialize --user=mysql --basedir=/export/server/mysql --datadir=/export/server/mysql/data 2>&1 | tee /tmp/mysqld.log | grep password | awk '{print $NF}' > /tmp/mysql_temp_password.txt
bin/mysql_ssl_rsa_setup --datadir=/export/server/mysql/data &> /dev/null
cat >/etc/my.cnf<<EOF
[mysqld]
port=3306
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
character_set_server=utf8
collation-server=utf8_unicode_ci
EOF
cat >/etc/systemd/system/mysqld.service<<EOF
[Unit]
Description=MySQL Server
After=network.target
[Service]
User=mysql
Group=mysql
Type=forking
# MySQL 执行命令及路径
ExecStart=/export/server/mysql/bin/mysqld --daemonize --pid-file=/export/server/mysql/data/mysqld.pid
ExecStop=/export/server/mysql/bin/mysqladmin --defaults-file=/export/server/mysql/my.cnf shutdown
# Ensure MySQL has sufficient time to start up
TimeoutSec=600
# PID 文件路径
PIDFile=/export/server/mysql/data/mysqld.pid
# Enable these options to auto-restart the service if it crashes
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
echo "正在刷新后台服务,然后启动mysqld..."
systemctl daemon-reload
systemctl start mysqld
systemctl enable mysqld
#10.重置mysql管理员密码为123456
echo "正在重置mysql管理员密码..."
cd /export/server/mysql
temp_password=`cat /tmp/mysql_temp_password.txt`
bin/mysqladmin -uroot password '123456' -p"$temp_password"
echo 'export PATH=$PATH:/export/server/mysql/bin' >> /etc/profile
source /etc/profile
mysql -V
echo "MySQL8安装成功,安装路径:/export/server/mysql,数据库初始密码:123456"
注意:执行完install-mysql8.sh安装脚本后,需要在Linux命令行执行source /etc/profile
初始化数据库(强烈建议)
建议把从服务器的data删除,然后重新同步
如果/etc/my.cnf有传统主从的配置,那么就要先删除对应的配置。
初始化数据库
systemctl stop mysqld
rm -rf /export/server/mysql/data/*
rm -rf /tmp/mysqld.log
/export/server/mysql/bin/mysqld --initialize --user=mysql --basedir=/export/server/mysql &>/tmp/mysqld.log
grep password /tmp/mysqld.log | awk '{print $NF}'
mysqld --user=mysql --skip-grant-tables --skip-networking &
mysql
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
FLUSH PRIVILEGES;
EXIT;
pkill mysqld
systemctl start mysqld
systemctl status mysqld --no-pager
mysql -uroot -p'123456'
rsync 同步 data 目录与删除 auto.cnf 文件
同步
# 1、停止 master服务器 上的 MySQL 服务(可选)
systemctl stop mysqld
# 2、在 master 上执行 rsync 同步 data 目录(可选)
rsync -av --delete /export/server/mysql/data/ mysql-node2:/export/server/mysql/data/
# 3、在 master 或 slave 删除 auto.cnf 文件
rm -f /export/server/mysql/data/auto.cnf
修改配置文件/etc/my.cnf
修改master
cat >/etc/my.cnf<<EOF
[mysqld]
# 基础路径
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
port=3306
# 日志
log-error=/export/server/mysql/master.err
log-bin=/export/server/mysql/data/binlog
server-id=101
# 字符集
character_set_server=utf8mb4
collation-server=utf8mb4_unicode_ci
# GTID 配置
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
binlog_format=ROW
binlog_row_image=FULL
sync_binlog=1
expire_logs_days=7
# 复制过滤:不同步的系统库
binlog-ignore-db=information_schema
binlog-ignore-db=mysql
binlog-ignore-db=performance_schema
binlog-ignore-db=sys
# InnoDB 优化
default_storage_engine=InnoDB
innodb_buffer_pool_size=1G
innodb_log_file_size=256M
innodb_log_buffer_size=64M
innodb_flush_log_at_trx_commit=1
innodb_file_per_table=1
# 连接配置
max_connections=500
max_connect_errors=1000000
table_open_cache=2000
# 慢查询日志
slow_query_log=1
slow_query_log_file=/export/server/mysql/slow.log
long_query_time=1
[client]
socket=/tmp/mysql.sock
EOF
修改slave
cat >/etc/my.cnf<<EOF
[mysqld]
# 基础路径
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
port=3306
# 日志
log-error=/export/server/mysql/slave.err
log-bin=/export/server/mysql/data/binlog
relay-log=/export/server/mysql/data/relaylog
relay-log-index=/export/server/mysql/data/relaylog.index
server-id=102
# 字符集
character_set_server=utf8mb4
collation-server=utf8mb4_unicode_ci
# GTID 配置
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
binlog_format=ROW
binlog_row_image=FULL
sync_binlog=1
expire_logs_days=7
# 复制过滤:不同步的系统库
replicate-wild-ignore-table=mysql.%
replicate-wild-ignore-table=information_schema.%
replicate-wild-ignore-table=performance_schema.%
replicate-wild-ignore-table=sys.%
# 从库只读
read_only=ON
super_read_only=ON
skip-slave-start=1 # 避免重启时自动启动复制线程,需手动控制
# 复制优化
relay_log_recovery=1
sync_relay_log=0
sync_relay_log_info=0
sync_master_info=0
# InnoDB 优化(适合从库,查询压力大)
default_storage_engine=InnoDB
innodb_buffer_pool_size=1G
innodb_log_file_size=256M
innodb_log_buffer_size=64M
innodb_flush_log_at_trx_commit=2 # 从库可设置为 2,提高复制与查询性能
innodb_file_per_table=1
# 连接配置
max_connections=500
max_connect_errors=1000000
table_open_cache=2000
# 慢查询日志(从库常用于报表分析)
slow_query_log=1
slow_query_log_file=/export/server/mysql/slow.log
long_query_time=1
[client]
socket=/tmp/mysql.sock
EOF
上面的从库只读等于
SET @@GLOBAL.read_only = ON;
锁表也差不多(flush tables with read lock;)(unlock tables;)
master节点/slave节点,优化配置,重启MySQL
# master 主服务器
touch /export/server/mysql/master.err
chown -Rf mysql:mysql /export/server/mysql
systemctl restart mysqld
systemctl status mysqld --no-pager
# slave 从服务器
touch /export/server/mysql/slave.err
chown -Rf mysql:mysql /export/server/mysql
systemctl restart mysqld
systemctl status mysqld --no-pager
创建账号
主服务器创建账号
CREATE USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
flush privileges;
启动同步
从库执行操作(需要调整为自己master服务器信息
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = host,
SOURCE_PORT = port,
SOURCE_USER = user,
SOURCE_PASSWORD = password,
SOURCE_AUTO_POSITION = 1;
SOURCE_AUTO_POSITION = 1: 这告诉从服务器使用自动位置跟踪功能,以便它可以自动从主服务器获取最新的二进制日志事件,而无需手动指定位置。
开启从库复制
start replica;
查看从库复制状态
show slave status\G
测试主从复制结果
主库中加入数据
create database if not exists db_itheima;
show databases;
use db_itheima;
create table if not exists students(
id int primary key,
name varchar(20)
) default charset=utf8;
insert into students values (10, 'lisa');
insert into students values (11, 'Rose');
select * from students;
show databases;
在从库查看数据
show databases;
use db_itheima;
show tables;
select * from students;
从库中查看复制状态,这张表记录了本机已经执行过的所有 GTID 事务区间
select * from mysql.gtid_executed;