主从复制的工作流程:主库将用户所有的写操作(增删改,查除外)记录到 binlog 日志当中并且生成一个 log dump
线程,从库生成 I/O
和 SQL
线程,从库的 I/O
线程向主库的 log dump
线程以 I/O
流的形式发送请求,主库的 log dump
线程收到从库 I/O
线程的请求后将 binlog
日志发送给从库,从库I/O线程收到binlog日志后将其写道relay log(中继日志)文件中,再由从库的SQL线程将 relay log 文件中的日志解析成 sql 脚本,最后执行生成的 sql
脚本文件,来实现主从的操作一致,达到最终数据一致的目的。
1.配置说明
准备两台安装了 mysql
的主机,要求存储数据完全一样
名称 | IP |
node01(主人 master) | 192.168.100.112 |
node02(从属 slave) | 192.168.100.240 |
2.修改数据库 uuid
由于 node02
是从虚拟机克隆而来,需要更改 uuid
,否则配置过程中会报错
# cd到mysql下的data目录,修改slave主机mysql的uuid
vi auto.cnf
3.配置步骤
同时在node01
和node02
两台机器上创建同步测试库sync
,创建一张测试空表seller
create database sync;
use sync;
-- 销售表(seller)
create table seller(
id int UNSIGNED not null auto_increment COMMENT 'ID',
saleNo char(3) not null COMMENT '编号',
saleName varchar(20) not null COMMENT '姓名',
sex char(1) DEFAULT'男' COMMENT '性别',
birthday date COMMENT '出生年月',
hireDate date COMMENT '雇员日期',
address varchar(50) COMMENT '地址',
telephone varchar(20) COMMENT '电话',
PRIMARY KEY (id),
UNIQUE ux_saleNo(saleNo),
INDEX ix_saleName(saleName)
)ENGINE=INNODB DEFAULT charset=utf8mb4;
node01
主库配置
修改my.cnf
文件
vi /etc/my.cnf
# 添加或修改以下内容
# 数据库id
server-id = 1
# 待同步的数据库日志
binlog-do-db = sync
# 设置无需同步的数据库日志
binlog-ignore-db = mysql
创建专门用于主从复制用户账号。因此使用root
账户登录mysql
,并执行一些指令:
# 登录msyql
mysql -u root -p
#创建sync_user用户用于数据同步,注意这里的ip是从库服务器的ip!
CREATE USER 'sync_user'@'192.168.100.240' IDENTIFIED WITH mysql_native_password BY '123456';
# 给主从复制账号授权
grant replication slave on *.* to 'sync_user'@'192.168.100.240';
重启主库的mysql
service mysql restart
再次登录mysql
,使用如下指令查看`master的状态:
show master status;
结果类似如下:我们需要关注File
和Position
的信息,后面要用到。
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000006 | 157 | sync | mysql | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
node02
从库配置
修改my.cnf
文件
vi /etc/my.cnf
# 添加或修改以下内容
# 数据库id必须与主库不同!!!
server-id=2
# 待同步的数据库
replicate-do-db=sync
# 设置无需同步的数据库
replicate-ignore-db=mysql,information_schema,performance_schema
然后我们重启从库的mysql
service mysql restart
最后实现主从同步,在从库具体配置如下:
# 先登录mysql,然后执行后续代码
mysql -u root -p
# 关闭从库
stop slave;
# 设置同步,注意这里是主库ip,日志名称和位置是我们之前上图中看到的名称和位置
change master to master_host='192.168.100.112',master_user='sync_user',master_password='123456',master_log_file='mysql-bin.000006',master_log_pos=157;
# 开启从库
start slave;
# 检查服务器状态
show slave status \G;
检测服务器状态正常情况下如下:
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 192.168.100.112
Master_User: sync_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 157
Relay_Log_File: node01-relay-bin.000002
Relay_Log_Pos: 326
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 157
Relay_Log_Space: 537
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 730adb84-f2da-11ec-8abe-000c290b9bf5
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
Master_public_key_path:
Get_master_public_key: 0
Network_Namespace:
Slave_IO_Running: Yes
,
Slave_SQL_Running: Yes
,
Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
三个项目正常说明配置成功
4.测试同步是否成功
在node01
的sync
库中的测试表插入以下数据,检验node02
是否将数据同步同步过去
INSERT seller(id,saleNo,saleName,sex,birthday,hireDate,address,telephone)
VALUE(1,'s01','王强','男','1975-12-08','2002-05-01','蓝色港湾42-12','0519-85150900'),
(2,'S02','付芳芳','女','1982-02-19','2008-08-14','燕阳花园53-4','0519-85150919'),
(3,'S03','李芳','女','1983-08-30','2008-04-01','富都小区252-16','0519-85150903'),
(4,'SO4','胡宝林','男','1991-09-19','2014-05-13','燕兴小区79-42','0519-85150903'),
(5,'S05','吴韵','男','1979-07-02','2008-11-15','富琛花园3-2','0519-85150904'),
(6,'S06','陆海成','男','1990-03-17','2014-04-17','都市雅居15-10','0519-85150905'),
(7,'S07','刘洋','男','1987-12-06','2012-10-23','顺园八村59-6','0519-85150906'),
(8,'S08','吴永佳','男','1985-07-10','2012-10-23','顺园三村21-12','0519-85150907');
5.异步复制的问题
以上介绍的主从复制是 mysql
的默认复制方式:异步复制。
在复制过程当中,主库不会去验证 bin-log
有没有成功复制到从库。如果主库提交一个事务并写入 bin-log
中后,由于网络原因,从库没有接收到对应的 bin-log
那从库就不会得到这个事务,也就造成了主从数据的不一致。
6.半同步复制搭建
在半同步模式中一个事务的执行流程如下:
1)主库收到客户端提交的事务。
2)主库执行事务。
3)将数据库事件写入到主库的 bin-log
日志中。
4)当从库来请求 bin-log
日志时,将其发送给从库。
5)从库的 I/O
线程获取到的 bin-log
日志写入到 relay-log
中。
6)当 relay-log
写入完成后,从库向主库发送一个写入成功的通知。
7)主库收到从库发来的成功通知后将事务的结果返回给客户端。
8)从库的SQL
线程对写入成功的 relay-log
进行重放,将数据保存到数据库中。
同步体现在主库返回事务结果给客户端时,须收到从库发送的 relay-log
写入成功的通知才能返回,否则会一直等待从库发来通知,直到等待时间到了(rpl_semi_sync_master_timeout
参数设置的值)。
异步体现在主库只要收到从库发来的成功通知即可返回结果给客户端,不需要考虑从库的 relay-log
是否重放成功。
假如主库在执行第 1、2、3 步的任意一步步骤时主库宕机,事务都不会成功,自然从库也不会收到主库发送的 bin-log
,所以主从的数据还是保持一致。
假如执行第 4 步时,主库宕机事务也不会提交成功,如果由于网络原因导致从库未接收到主库的 bin-log
日志,则主库会等待从库一段时间(等待从库写入 relay-log
成功的通知 ACK),然后将主从复制的模式切换到异步模式,再返回成功给客户端。
假如执行第 5 步时,主库宕机由于此时主库还在等待从库,则主库事务自然执行失败。如果从库宕机则主库会等待从库,若一段时间后未收到 ACK,则将主从复制的模式切换到异步模式,再返回成功给客户端。
安装插件
方法一:修改配置文件
主库设置
# 主库进入sql页面
install plugin rpl_semi_sync_master soname 'semisync_master.so';
# vi /etc/my.cnf添加以下参数,开启主库半同步复制
rpl_semi_sync_master_enabled=1
# 重启mysql服务
service mysql restart
从库设置
# 从库sql页面输入
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
# 开启半同步复制
rpl_semi_sync_slave_enabled=1
SET GLOBAL rpl_semi_sync_slave_enabled = on;
# 重启mysql服务
service mysql restart
方法二:mysql 用户界面调整参数
主库
# 主库进入sql页面
install plugin rpl_semi_sync_master soname 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = on;
从库
# 从库sql页面输入
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = on;
# 从节点重启IO线程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
验证半同步复制
主从库分别输入show status like "%rpl_semi%";
#主库显示如下
mysql> show status like "%rpl_semi%";
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
# 从库显示如下
mysql> show status like "%rpl_semi%";
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.00 sec)
Rpl_semi_sync_master_clients
为1
Rpl_semi_sync_master_status
为ON
Rpl_semi_sync_slave_status
为ON
即主从半同步复制正常启动;
常用配置
主库使用 show variables like '%Rpl%';
查看半同步参数信息
mysql> show variables like '%Rpl%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_read_size | 8192 |
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
| rpl_stop_replica_timeout | 31536000 |
| rpl_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
9 rows in set (0.01 sec)
1)rpl_semi_sync_master_enabled=ON
表示主库开启半同步复制。默认为 OFF
,即关闭半同步复制。
2)rpl_semi_sync_master_timeout=10000
单位为毫秒,默认为 10 秒。用于配置主库等待从库写入 relay-log
日志成功的 ACK
通知的时间。若超过该时间主库将主从模式切换为异步模式,并返回事务提交成功给客户端。
3)rpl_semi_sync_master_wait_for_slave_count=1
表示主库收到 1 个从库发来的 ACK
就返回事务提交成功。默认为 1 。这个参数一般与 rpl_semi_sync_master_wait_no_slave
参数配合使用,默认为 ON。
-
rpl_semi_sync_master_wait_no_slave
为OFF时,只要master
发现Rpl_semi_sync_master_clients
小于rpl_semi_sync_master_wait_for_slave_count
(master
提交事务后所需的应答数量),则master
立即转为异步模式。 -
rpl_semi_sync_master_wait_no_slave
为ON时,空闲时间(无事务提交)里,即使master发现Rpl_semi_sync_master_clients
小于rpl_semi_sync_master_wait_for_slave_count
,也不会做任何调整。只要保证在事务超时之前,master
收到大于等于rpl_semi_sync_master_wait_for_slave_count
值的ACK应答数量,master
就一直保持在半同步模式;如果在事务提交阶段(master
等待ACK)超时,master
才会转为异步模式。
无论 rpl_semi_sync_master_wait_no_slave
为 ON 还是 OFF ,当 slave
上线到 rpl_semi_sync_master_wait_for_slave_count
值时, master
都会自动由异步模式转为半同步模式。
4)rpl_semi_sync_master_wait_point=AFTER_SYNC
,用于配置主库在哪个点(什么时机)等待接收从库的 ACK 通知。默认为 AFTER_SYNC
。
AFTER_SYNC
表示主库在bin-log
被flush
之后,在存储引擎commit
前进入等待,这可以保证数据在被复制到从库前不被其他会话(客户端)可见。
- 假如现在主库有 A、B 两个会话(客户端)连接着,A 修改了一些数据,那么在主库收到从库的 ACK 前,这些数据还没有
commit
到主库的存储引擎中,所以 B 会话还看不到更改后的数据。
-
AFTER_COMMIT
表示主库bin-log
被flush
之后并在存储引擎commit
之后进入等待,尽管发起commit
的会话还未收到commit
成功的结果,其他的会话已经可以看到commit
后的数据了。
参考文档
[1] MySQL8主从复制(一主一从)配置搭建详解 _ 潘子夜个人博客 (panziye.com)
[2] (34条消息) mysql8 进阶(三) 主从复制之半同步复制_蜜汁坤丝的博客
[3] rpl_semi_sync_master_wait_no_slave 参数研究实验 - 孔个个
[4] MySQL主从复制之半同步(semi-sync replication) (baidu.com)