1、半同步介绍:
(1)从MySQL5.5开始,MySQL以插件的形式支持半同步复制。如何理解半同步呢?首先我们来看看异步,全同步的概念;
1)异步复制(Asynchronous replication):
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接
收并处理,这样就会有一个问题,主如果主宕掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升
为主,可能导致新主上的数据不完整。
2)全同步复制(Fully synchronous replication):
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所
以全同步复制的性能必然会收到严重的影响,保证了数据的一致性。
3)半同步复制(Semisynchronous replication):
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到
relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少
是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
(2)半同步复制的原理图:
(3)半同步复制的潜在问题:
客户端事务在存储引擎层提交后,在得到从库确认的过程中,主库宕机了,此时,可能的情况有两种:
1)事务还没发送到从库上:
此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动后,以从库的身份
重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的。
2)事务已经发送到从库上:
此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上。
(4)无数据丢失的半同步复制:
针对上述潜在问题,MySQL 5.7引入了一种新的半同步方案:Loss-Less半同步复制。针对上面这个
图“Waiting Slave dump”被调整到“Storage Commit”之前。当然,之前的半同步方案同样支
持,MySQL 5.7.2引入了一个新的参数进行控制rpl_semi_sync_master_wait_point;
rpl_semi_sync_master_wait_point有两种取值:
AFTER_SYNC #新的半同步方案;
AFTER_COMMIT #老的半同步方案,如上图所示;
(5)小结:
1)半同步或全同步:
主库掉电,从库不掉电,不丢事物;
主库不掉电,从库掉电,不会丢事物,因为主库此时也无法提交事物;
如果同时掉电会丢事物;
2)异步同步:
主库掉电,从库不掉电,事物丢失;
主库不掉电,从库掉电,事物丢失;
如果同时掉电,会丢事物;
3)说明以上的事物丢失是针对从库而言;
2、半同步安装条件:
(1)mysql5.5及以上的版本;
(2)变量have_dynamic_loading为YES(默认为YES);
(3)异步复制已经存在;
(4)主从mysql服务器,我这里使用的是在一台机器上的多实例,端口号分别是3306和3307;
3、半同步实现方式一:
停止主和从,修改主从的my.cnf配置文件添加如下几行;
[mysqld]
plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
#加载半同步的主和从插件,默认是不加载的;
rpl-semi-sync-master-enabled = ON
#开启主的半同步,默认不开启;
rpl-semi-sync-slave-enabled = ON
#开启从的半同步功能,默认不开启,只有使用了CHANG后该参数ON才生效;
启动主和从即可完成主从的半同步;
4、半同步实现方式二:
在不停止主从数据库实例的情况下进行配置;
(1)主库:
#检查动态加载功能是否为YES,默认是YES;
mysql> show variables like 'have_dynamic_loading';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| have_dynamic_loading | YES |
+----------------------+-------+
1 row in set (0.00 sec)
#安装半同步插件;
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)
#检查插件是否安装成功;
mysql> show plugins;
+--------------------------+----------+--------------------+--------------------+---------+
| Name | Status | Type | Library | License |
+--------------------------+----------+--------------------+--------------------+---------+
| rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL |
+--------------------------+----------+--------------------+--------------------+---------+
22 rows in set (0.00 sec)
#开启半同步复制,默认是关闭的状态;
mysql> set global rpl_semi_sync_master_enabled = ON;
Query OK, 0 rows affected (0.00 sec)
#检查环境变量:
mysql> show variables like '%semi%';
+------------------------------------+-------+
| Variable_name | Value |
+------------------------------------+-------+
| rpl_semi_sync_master_enabled | ON |
#表示开启半同步复制;
| rpl_semi_sync_master_timeout | 10000 |
#默认1000毫秒,即10秒超时,将切换为异步复制;
| rpl_semi_sync_master_trace_level | 32 |
表示用于开启半同步复制时的调试级别,默认32;
| rpl_semi_sync_master_wait_no_slave | ON |
#表示是否允许master每个事物都要等待slave接收确认;
+------------------------------------+-------+
4 rows in set (0.00 sec)
(2)从库:
#检查动态加载功能是否为YES,默认是YES;
mysql> show variables like 'have_dynamic_loading';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| have_dynamic_loading | YES |
+----------------------+-------+
1 row in set (0.00 sec)
#安装半同步插件;
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.00 sec)
#检查插件是否安装成功;
mysql> show plugins;
+--------------------------+----------+--------------------+-------------------+---------+
| Name | Status | Type | Library | License |
+--------------------------+----------+--------------------+-------------------+---------+
| rpl_semi_sync_slave | ACTIVE | REPLICATION | semisync_slave.so | GPL |
+--------------------------+----------+--------------------+-------------------+---------+
22 rows in set (0.00 sec)
#开启半同步复制,默认是关闭的状态;
mysql> set global rpl_semi_sync_slave_enabled = ON;
Query OK, 0 rows affected (0.00 sec)
#检查环境变量;
mysql> show variables like '%semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
#表示开启半同步复制;
| rpl_semi_sync_slave_trace_level | 32 |
#表示用于开启半同步复制时的调试级别,默认32;
+---------------------------------+-------+
2 rows in set (0.00 sec)
#重启服务器IO线程,手动将异步模式切换为半同步模式;
mysql> stop slave io_thread;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave io_thread;
(3)修改主和从的配置参数,以便在重启mysql实例后配置的参数还能够使用:
[mysqld]
plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
#加载半同步的主和从插件,默认是不加载的;
rpl-semi-sync-master-enabled = ON
#开启主的半同步,默认不开启;
rpl-semi-sync-slave-enabled = ON
#开启从的半同步功能,默认不开启,只有使用了CHANG后该参数才生效;
5、检查状态变量:
(1)主库:
mysql> show global status like '%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) Query OK, 0 rows affected (0.00 sec)
(2)从库:
mysql> show global status like '%semi%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
#表示从库正在运行的是异步复制;
+----------------------------+-------+
1 row in set (0.00 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.16.1.41
Master_User: lc
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000010
Read_Master_Log_Pos: 107
Relay_Log_File: relay-log.000018
Relay_Log_Pos: 253
Relay_Master_Log_File: mysql-bin.000010
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: 107
Relay_Log_Space: 549
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
1 row in set (0.00 sec)
ERROR:
No query specified
6、总结:
(1)在一主多从的架构中,如果要开启半同步复制,并不要求所有的从都是半同步复制;
(2)MySQL 5.7极大的提升了半同步复制的性能。
5.6版本的半同步复制,IO线程承担了两份不同且又十分频繁的任务:传送binlog 给slave ,还需要等待slave
反馈信息,而且这两个任务是串行的,IO线程必须等待slave返回之后才会传送下一个events事务。IO线程已
然成为整个半同步提高性能的瓶颈。在高并发业务场景下,这样的机制会影响数据库整体的TPS 。
5.7版本的半同步复制中,独立出一个ack collector thread ,专门用于接收slave 的反馈信息。这样master上有两个线
程独立工作, 可以同时发送binlog到slave,和接收slave的反馈。
(3)事实上,半同步复制并不是严格意义上的半同步复制:
当半同步复制发生超时时(由rpl_semi_sync_master_timeout参数控制,单位是毫秒,默认为10000,即10s),会暂时
关闭半同步复制,转而使用异步复制。当IO线程发送完一个事务的所有事件之后,在超时时间内收到了从库的响应,则主从
又重新恢复为半同步复制。