MySQL复制默认是异步复制,存在一定的概率备库与主库的数据是不对等的,如果Master宕机,事务在Master上已提交,但很可能这些事务没有传到任何的Slave上,此时Slave也可能会丢失事务。在半同步复制的架构下,当master在将自己binlog发给slave上的时候,要确保slave已经接受到了这个二进制日志以后,才会返回数据给客户端。

 

半同步复制原理 


半同步复制架构图如下所示:

MySQL gtid半同步复制详解与主从搭建_同步复制

半同步复制的概念:

1,当Slave主机连接到Master时,能够查看其是否处于半同步复制的机制。

2,当Master上开启半同步复制的功能时,至少应该有一个Slave开启其功能。此时,一个线程在Master上提交事务将受到阻塞,直到得知一个已开启半同步复制功能的Slave已收到此事务的所有事件,或等待超时。

3,当一个事务的事件都已写入其relay-log中且已刷新到磁盘上,Slave才会告知已收到。在 Master 实例上,有一个专门的线程(ack_receiver)接收备库的响应消息,并以通知机制告知主库备库已经接收的日志,可以继续执行。

4,如果等待超时,也就是Master没被告知已收到,此时Master会自动转换为异步复制的机制。当至少一个半同步的Slave赶上了,Master与其Slave自动转换为半同步复制的机制。

5,半同步复制的功能要在Master,Slave都开启,半同步复制才会起作用;否则,只开启一边,它依然为异步复制。

6,半同步特性的出现,就是为了保证在任何时刻主备数据一致的问题。相对于异步复制,半同步复制要求执行的每一个事务,都要求至少有一个备库成功接收后,才返回给用户。

事物在主库执行完binlog后接受到从库的ACK,才会回复客户端。所以,相比而言,性能有所降低。

基于gtid主从复制简介:
mysql数据库从5.6.5开始新增一种基于gtid的复制方式。gtid(global transaction id)是对于一个已提交事务的编号。gtid实际上是由uuid+tid组成的,其中uuid是mysql实例的一个标识,tid则代表了该实例上交的事务数量,并且随着事务的提交单调递增。

主从复制默认是通过pos(position)复制,就是说在日志文档里,将用户进行的每一项操作都进行编号(pos),每一个事件都有一个起始编号,一个终止编号,在配置主从复制节点时,要求其从master的pos开始同步数据库里面的数据,这也称作传统复制技术。

gtid就是类似pos的作用,不过它是整个mysql复制架构全局通用的,即在整个mysql冗余架构中,它们的日志文件里面事件的gtid的数值是一致的。

gtid是一个对于已提交的事物的编号,并且是一个全局唯一的编号。通过gtid保证每个主库上提交的事务在集群中有一个唯一的id。这种方式强化了主备的一致性,故障恢复及其容错能力。

pos与gtid的区别
两者都是日志文件里的一个标志,如果将整个mysql集群看作一个整体,pos就是局部的,gtid就是全局的。

 

半同步搭建


环境: 

主机名

IP

系统

角色

db01

192.168.179.102

CentOS7.4

Master

db02

192.168.179.103

CentOS7.4

Slave

#主库配置
[root@localhost ~]# sed -e '/#/d' -e '/^$/d' /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

server_id=1
gtid-mode = on
enforce-gtid-consistency = 1
log-bin=master-bin
binlog_format = row
log-slave-updates = 1

#从库配置
[root@localhost ~]# sed -e '/#/d' -e '/^$/d' /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

server_id=2
gtid-mode = on
enforce-gtid-consistency = 1
log-bin=slave-bin
binlog_format = row
log-slave-updates = 1

#修改完配置文件以后重启数据库实例






# 主库查看GTID开启状态
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed_compression_period | 1000 |
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+
# 从库查看GTID开启状态
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed_compression_period |
|
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+


#主库安装半同步插件 -------------------------------
mysql> 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_sync%';
+----------------------+---------------+
| plugin_name | plugin_status |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE |
+----------------------+---------------+
注:如果想卸载半同步插件,可以使用如下命令:
mysql> uninstall plugin rpl_semi_sync_master;

#启用插件
mysql> set global rpl_semi_sync_master_enabled=on;
#设置超时时间
mysql> set global rpl_semi_sync_master_timeout=1000;
mysql> show global variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 1000 |
| 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_semi_sync_master_enabled=1的效果,第一行是ON则表示半同步复制已经开启。

#rpl_semi_sync_master_enabled是控制Master是否开启半同步,开启或不开启,将其设置为ON或OFF(1or0).
#rpl_semi_sync_master_timeout是控制Master等待多长时间被告知Slave已收到,也就是所谓的超时时间。



#从库安装半同步插件 -------------------------------
mysql> 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_sync%';
+---------------------+---------------+
| plugin_name | plugin_status |
+---------------------+---------------+
| rpl_semi_sync_slave | ACTIVE |
+---------------------+---------------+

mysql> set global rpl_semi_sync_slave_enabled= 1;
mysql> show global variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | OFF |
| 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_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+-------------------------------------------+------------+
#重启io_thread,使用相同步骤配置从节点,完成后需要重启io_thread,不重启当执行时会超时,超时后则自动降为异步:
mysql> stop slave io_thread;
mysql> start slave io_thread;

#rpl_semi_sync_slave_enabled是控制Slave是否开启半同步,开启或不开启,将其设置为ON或OFF(1or0)。

#主库查看未开启gtid状态
mysql> SHOW PROCESSLIST \G
*************************** 1. row ***************************
Id: 2
User: root
Host: localhost
db: NULL
Command: Query
Time: 0
State: starting
Info: SHOW PROCESSLIST
1 row in set (0.00 sec)


# 主库创建复制用户 --------------------------------------------------------------
mysql> grant replication slave on *.* to repl@'%' identified by '123456';


# 从库建立复制通道---------------------------------------------------------------
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.179.102',
-> MASTER_USER='repl',
-> MASTER_PASSWORD='123456',
-> MASTER_PORT=3306,
-> MASTER_CONNECT_RETRY=10,
-> MASTER_AUTO_POSITION=1;
mysql> start slave;

写入你的配置文件,避免宕机之后重启失效 

以上的启动方式是在命令行操作,也可写在配置文件中。

主:
plugin-load=rpl_semi_sync_master=semisync_master.so
rpl_semi_sync_master_enabled=1

从:
plugin-load=rpl_semi_sync_slave=semisync_slave.so
rpl_semi_sync_slave_enabled=1
在有的高可用架构下,master和slave需同时启动,以便在切换后能继续使用半同步复制


#如果不配置在配置文件里面从库宕机重启不会启用半同步复制。会出现如下:
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF |
+----------------------------+-------+

查看是否成功开启GTID同步,Command显示Binlog Dump GTID表示成功开启GTID同步。

mysql> SHOW PROCESSLIST \G
*************************** 3. row ***************************
Id: 4
User: repl
Host: 192.168.179.103:59046
db: NULL
Command: Binlog Dump GTID
Time: 146
State: Master has sent all binlog to slave; waiting for more updates
Info: NULL

#查看主库和从库上的半同步复制是否在运行
mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON |
+-----------------------------+-------+
#登录从库查看
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+

mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.179.102
Master_User: repl
Master_Port: 3306
Connect_Retry: 10
Master_Log_File: master-bin.000001
Read_Master_Log_Pos: 429
Relay_Log_File: localhost-relay-bin.000002
Relay_Log_Pos: 644
Relay_Master_Log_File: master-bin.000001
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: 429
Relay_Log_Space: 855
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: 18f5da07-a096-11ea-8c70-000c290e1abf
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave 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: 18f5da07-a096-11ea-8c70-000c290e1abf:1
Executed_Gtid_Set: 18f5da07-a096-11ea-8c70-000c290e1abf:1
Auto_Position: 1
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:

在此过程出现问题和解决方式:
问题1:Slave_SQL_Running: No

解决方法:start slave; # 启动slave(或者关闭slave之后,---> reset slave,再启动slave)

问题2:Slave_IO_Running: Connecting,说明主从库没有连接上
解决方法:1) 在master中是否对用户进行授权

2) 密码或pos号是否正确

3) 防火墙是否关闭

监控半同步复制的状态变量(几个常用的):

mysql> show status like '%semi_sync%';
+--------------------------------------------+-------+
| 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 | 1 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 1 |
| 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 |
+--------------------------------------------+-------+

Rpl_semi_sync_master_clients:查看有多少个开启半同步复制的插件的Slave

Rpl_semi_sync_master_status:查看在Master上半同步复制是否正在运行,其值为ON时,说明Master已启用半同步且已被告知有Slave收到;其值为OFF时,说明Master没启用半同步或是没被告知,由于timeout等原因。

Rpl_semi_sync_master_no_tx:查看有多少事务没有用半同步复制的机制进行复制。

Rpl_semi_sync_master_yes_tx:查看有多少事务是通过半同步复制机制成功复制。

Rpl_semi_sync_slave_status:查看Slave上半同步复制是否正常运行,其值为ON时,说明Slave正通过半同步复制且Slave I/O正在运行;为OFF时,反之。

 

验证半同步复制是否正常


验证方法:正常在主库上创建一张表,会立刻返回,耗时0.1s。关闭从库的io线程,然后在主库上执行建表操作,会发现,主库上回阻塞10秒之后才会返回,而这个时间正好和主库上的rpl_semi_sync_master_timeout相同,表示半同步起作用了,主库的DDL操作需要等到从库应用完relaylog之后才返回

#从库执行:
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
mysql> STOP SLAVE IO_THREAD;
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF |
+----------------------------+-------+

#主库执行:
mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON |
+-----------------------------+-------+
mysql> insert into test values(6666);
Query OK, 1 row affected (10.00 sec)
#主库上回阻塞10秒之后才会返回

mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF |
+-----------------------------+-------+

mysql> show master status\G
*************************** 1. row ***************************
File: master-bin.000001
Position: 3139
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 18f5da07-a096-11ea-8c70-000c290e1abf:1-12


#从库执行,重新打开IO线程可以看到在主库没有同步的事务同步到从库了
mysql> START SLAVE IO_THREAD;
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
[root@localhost ~]# mysqlbinlog /var/lib/mysql/slave-bin.000001 | tail -n 50
# at 904
#200605 16:22:01 server id 1 end_log_pos 969 CRC32 0xe29e844e GTID last_committed=3 sequence_number=4 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:12'/*!*/;
# at 969
#200605 16:22:01 server id 1 end_log_pos 1032 CRC32 0x50b908b4 Query thread_id=18 exec_time=336 error_code=0
SET TIMESTAMP=1591345321/*!*/;
BEGIN
/*!*/;
# at 1032
#200605 16:22:01 server id 1 end_log_pos 1083 CRC32 0x296d8336 Table_map: `students`.`test` mapped to number 108
# at 1083
#200605 16:22:01 server id 1 end_log_pos 1123 CRC32 0xf886bfea Write_rows: table id 108 flags: STMT_END_F

BINLOG '
qQDaXhMBAAAAMwAAADsEAAAAAGwAAAAAAAEACHN0dWRlbnRzAAR0ZXN0AAEDAAE2g20p
qQDaXh4BAAAAKAAAAGMEAAAAAGwAAAAAAAEAAgAB//4KGgAA6r+G+A==
'/*!*/;
# at 1123
#200605 16:22:01 server id 1 end_log_pos 1154 CRC32 0x9faa662d Xid = 44
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

 

开启半同步复制意味着什么?


在主库开启一个事务,这个事务在主库和远端的从库各存一份。此时 Rpl_semi_sync_master_yes_tx 的值加一。忽然断网时,会有10s的hang住(rpl_semi_sync_master_timeout =10000),然后mysql会自己关闭主从复制。然后变成异步。此时Rpl_semi_sync_master_yes_tx 值不变了,而Rpl_semi_sync_master_no_tx 的值就开始加一。
 

总结:使用半同步复制机制,性能也许会受到影响,但其主要是为了维持数据完整性,安全性的的一个策略,虽会损失一点性能,但还是值得的。