MySQL半同步使用注意事项

最近遇到了一个关于半同步的问题,刚搭建的半同步数据库实例,执行第一个sql的时候,从库延迟太长,原因是从库开启了参数
rpl_semi_sync_master_enabled=on,同时设置的rpl_semi_sync_master_timeout为30分钟,接下来说下半同步使用注意事项以及该问题的具体原因,

一、开启半同步:必须主从库都得设置对应的参数!具体如下所示:
主库:
set global rpl_semi_sync_master_enabled = 1;
从库
set global rpl_semi_sync_slave_enabled = 1;
注意如果从库没设置这个rpl_semi_sync_slave_enabled参数,那么主库就不识别这个从库,但是主库还是增强半同步的方式,此时主库就会由于没有slave返回ack而夯住了,可以通过在master查看下面这个状态变量来了解此时有多少从库可以反馈ack!
如下所示的主库状态变量表示当前有多少个从库是处于半同步复制的方式来连接主库的,或者说当前有多少slave可以给主库反馈ack;
mysql> show  status  like   'Rpl_semi_sync_master_clients';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_master_clients | 0     |
+------------------------------+-------+
1 row in set (0.00 sec)
二、从库建议不开启master角色的增强半同步参数,但是需要开启slave角色的半同步参数;如果非要开启rpl_semi_sync_master_enabled需要在slave修改参数rpl_semi_sync_master_wait_no_slave=off;
rpl_semi_sync_slave_enabled=on-----从库需要开启,否则该slave不能作为半同步反馈给master的ack;
rpl_semi_sync_master_enabled=off ----如果考虑到主从切换,可以开启,但是slave第一次启动的时候,会因为没有slave反馈ack而处于夯住状态,所以建议修改参数
rpl_semi_sync_master_wait_no_slave=off 这样当发现她没有足够的slave的时候,立即退化为异步复制!
三、介绍相关参数,并合理设置相关参数;建议在配置文件中设置开头带loose开头的参数,这样如果你没有安装半同步插件,它依旧能启动数据库!
#loose_rpl_semi_sync_master_enabled = 1
#loose_rpl_semi_sync_slave_enabled = 1
#loose_rpl_semi_sync_master_timeout = 1800000
rpl_semi_sync_master_enabled:主库是否开启半同步。 可以动态调整!立即生效;
rpl_semi_sync_master_wait_point:同步时间点after_commit,after_sync。可以动态调整!对于新事务有效!

rpl_semi_sync_master_wait_for_slave_count:主库事务提交后需要从库的确认数量。--可以动态调整!立即生效!

rpl_semi_sync_master_timeout:主库等待从库的确认超时时间(ms),默认10000ms(也就是10秒),等待超过则半同步降级为异步模式。---可以动态调整!但是对于已经处于等待ack的事务无效!对修改后新的事务有效,并且当超时退化成半同步后,只要条件再次满足半同步了,就自动切换回半同步了(例如某一刻你没有了从库,然后主库由于超时退化成异步复制,等你再次有了从库后,自动切换回半同步)

rpl_semi_sync_master_wait_no_slave:----可以动态调整!立即生效!
为OFF时,只要主库发现(Rpl_semi_sync_master_clients)小于(rpl_semi_sync_master_wait_for_slave_count),则半同步立即转为异步模式;

为ON时,在无事务提交的空闲时间里,即使主库发现(Rpl_semi_sync_master_clients)小于(rpl_semi_sync_master_wait_for_slave_count),也不会做任何调整;

只要保证在事务超时之前,主库收到大于等于(rpl_semi_sync_master_wait_for_slave_count)值的ACK应答数量,主库就一直保持在半同步模式,如果在事务提交阶段(主库等待ACK)超时,半同步才会转为异步模式;

无论(rpl_semi_sync_master_wait_no_slave)为ON还是OFF,当slave上线到(rpl_semi_sync_master_wait_for_slave_count)值时,master都会自动由异步模式转为半同步模式。
四、注意一个情景:重启主库但是不想让他发生切换的时候需要注意,由于master停止,slave的IO线程都会断开,然后默认过60秒去重新连接master;在这60秒内(参数master-connect-retry控制),如果master接收了事务,那么此时master由于没有从库,就会导致master等待从库ACK,此时master就会夯住,一直到有slave连接上了master,或者等待时间超过rpl_semi_sync_master_timeout时间!
如果你不想让他等60秒自动恢复,解决办法:
方法1、主库执行关闭半同步的开关-----推荐该方法
mysql> set  global  rpl_semi_sync_master_enabled=off;
Query OK, 0 rows affected (0.00 sec)
方法2、主库设置rpl_semi_sync_master_wait_no_slave=off;---这样当master发现没有足够的从库的时候,就自动退化成异步复制---推荐使用
方法3、在从库手动重启slave的IO线程,手动让slave重连主库,而不是等60秒,这样主库就不会因为没有从库而夯住;
mysql> stop slave    io_thread;
mysql> start slave io_thread;
方法4、 停止业务,保证slave重连master的这段时间内主库没有接受任何操作;---不建议
五、主库状态变量Rpl_semi_sync_master_clients的意义;
1、首先需要源端开启半同步,这个参数才会有;
This variable is available only if the source-side semisynchronous replication plugin is installed.
2、这个参数表示当前连接着的从库的个数;但是这个状态变量在增加slave的时候是实时更新的,但是减少了slave的个数的时候不是实时更新的;
3、这个参数给slave的参数rpl_semi_sync_slave_enabled有关(或者说是和slave的状态变量Rpl_semi_sync_slave_status有关系),当slave的状态变量Rpl_semi_sync_slave_status=on的时候主库的Rpl_semi_sync_master_clients状态变量才会加1;当Rpl_semi_sync_slave_status=off的从库连接上主库后,主库的Rpl_semi_sync_master_clients状态变量不会加1,
4、动态调整参数slave参数rpl_semi_sync_slave_enabled后,需要restart  slave io  thread才能让slave的Rpl_semi_sync_slave_status动态变化,进而让主库Rpl_semi_sync_master_clients动态增减!
mysql> show  status   like   '%Rpl_semi_sync_slave_status%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)

mysql> show  variables  like   '%Rpl%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 1800000    |
| 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               | OFF        |
| rpl_semi_sync_slave_trace_level           | 32         |
| rpl_stop_slave_timeout                    | 31536000   |
+-------------------------------------------+------------+
9 rows in set (0.00 sec)
案例1:证明该状态变量的值给当前主库连接的slave个数有关;
主库查看当前是2个从库处于半同步复制中;
mysql> show status like '%Rpl_semi_sync_master_clients%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_master_clients | 2     |
+------------------------------+-------+
从库上执行stop  slave的操作;
然后再次去查看主库show status like '%Rpl_semi_sync_master_clients%';发现还是2;
等过了大概一分钟后,变成了1;再次查看发现变成了1;
mysql> show status like '%Rpl_semi_sync_master_clients%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_master_clients | 1     |
+------------------------------+-------+
1 row in set (0.00 sec)
案例2;当时当你再次start slave后,会发现这个参数瞬间就变成了2;
案例3:证明该状态变量的数值和他的从库的Rpl_semi_sync_slave_status参数有关系;
当你把其中一个从库Rpl_semi_sync_slave_status变为off后,再次去查看主库的Rpl_semi_sync_master_clients状态变量,会发现此时主库该状态变量就减少了1;
mysql> show status like '%Rpl_semi_sync_slave_status%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF   |
+----------------------------+-------+
1 row in set (0.00 sec)
再次查看该状态变量发现变成了1
mysql> show status like '%Rpl_semi_sync_master_clients%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_master_clients | 1     |
+------------------------------+-------+
1 row in set (0.00 sec)
六、从库状态变量Rpl_semi_sync_slave_status和rpl_semi_sync_slave_enabled参数的关系!
1)当前以rpl_semi_sync_slave_enabled=on的方式启动数据库后,Rpl_semi_sync_slave_status状态变量是on,然后动态调整rpl_semi_sync_slave_enabled=off,发现状态变量依旧是Rpl_semi_sync_slave_status=on!
2)同样rpl_semi_sync_slave_enabled=off的方式启动数据库后,Rpl_semi_sync_slave_status状态变量是off,然后动态调整rpl_semi_sync_slave_enabled=on,发现状态变量依旧是Rpl_semi_sync_slave_status=off!
3)除非你stop slave,然后start slave;然后状态变量才会随着rpl_semi_sync_slave_enabled参数动态调整而变化!准确的说是stop  slave  io_thread即可!注意单独stop slave  sql_thread是不可以的!
最开始状态:
mysql> show  variables  like   'rpl_semi_sync_master_enabled';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| rpl_semi_sync_master_enabled | ON    |
+------------------------------+-------+
1 row in set (0.00 sec)
mysql> show  status   like   '%Rpl_semi_sync_slave_status%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)
尝试调整参数rpl_semi_sync_master_enabled
mysql> set global  rpl_semi_sync_slave_enabled=0;
Query OK, 0 rows affected (0.00 sec)
再次查看发现状态变量还是on,等了一个晚上也查看也是on!
mysql> show  variables  like   'rpl_semi_sync_slave_enabled';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| rpl_semi_sync_slave_enabled | OFF   |
+-----------------------------+-------+
1 row in set (0.00 sec)
mysql> show  status   like   '%Rpl_semi_sync_slave_status%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)
执行stop slave,然后启动slave,发现状态变量变化了!
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show  status   like   '%Rpl_semi_sync_slave_status%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF    |
+----------------------------+-------+
1 row in set (0.00 sec)
结论:Rpl_semi_sync_slave_status状态变量只会在数据库启动的时候和rpl_semi_sync_slave_enabled参数保证一致!当你启动数据库后,动态调整参数rpl_semi_sync_slave_enabled后,需要restart slave io thread,Rpl_semi_sync_slave_status状态变量才会变化!
六、主库Rpl_semi_sync_master_status状态变量和参数rpl_semi_sync_master_enabled的关系,Rpl_semi_sync_master_status随着 rpl_semi_sync_master_enabled变化实时变化!区别于slave角色的参数rpl_semi_sync_slave_enabled和Rpl_semi_sync_slave_status状态变量的关系!
mysql> show  status   like   '%Rpl_semi_sync_master_status%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON   |
+-----------------------------+-------+
1 row in set (0.00 sec)
mysql> set  global  rpl_semi_sync_master_enabled=off;
Query OK, 0 rows affected (0.11 sec)
mysql> show  status   like   '%Rpl_semi_sync_master_status%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF   |
+-----------------------------+-------+
1 row in set (0.00 sec)
mysql> show  status   like   '%Rpl_semi_sync_master_status%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF   |
+-----------------------------+-------+
1 row in set (0.00 sec)
mysql> set  global  rpl_semi_sync_master_enabled=on;
Query OK, 0 rows affected (0.00 sec)
mysql> show  status   like   '%Rpl_semi_sync_master_status%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON    |
+-----------------------------+-------+
1 row in set (0.01 sec)
五、特殊讲解下参数:rpl_semi_sync_master_wait_no_slave

官方文档记录:
rpl_semi_sync_master_wait_no_slave

Command-Line Format
--rpl-semi-sync-master-wait-no-slave[={OFF|ON}]
System Variable
Scope
Global
Dynamic
Yes
Type
Boolean
Default Value
ON

Controls whether the source waits for the timeout period configured by rpl_semi_sync_master_timeout to expire, even if the replica count drops to less than the number of replicas configured by rpl_semi_sync_master_wait_for_slave_count during the timeout period.

When the value of rpl_semi_sync_master_wait_no_slave is ON (the default), it is permissible for the replica count to drop to less than rpl_semi_sync_master_wait_for_slave_count during the timeout period. As long as enough replicas acknowledge the transaction before the timeout period expires, semisynchronous replication continues.

When the value of rpl_semi_sync_master_wait_no_slave is OFF, if the replica count drops to less than the number configured in rpl_semi_sync_master_wait_for_slave_count at any time during the timeout period configured by rpl_semi_sync_master_timeout, the source reverts to normal replication.

This variable is available only if the source-side semisynchronous replication plugin is installed.

当rpl_semi_sync_master_wait_no_slave=on的时候,如果master探测到没有足够的slave(rpl_semi_sync_master_wait_for_slave_count参数决定)可以反馈ack后,就自动退化成异步复制,当再次有足够个数的slave可以反馈ack后,master就自动又切换成了半同步!
当rpl_semi_sync_master_wait_no_slave=off的时候,如果没有足够的slave可以反馈ack, 进而master夯住一直等待rpl_semi_sync_master_timeout参数设置的时间后,退化成异步复制;等再次有足够的slave可以反馈ack了,master就不会自动切换成半同步!

请使用浏览器的分享功能分享到微信等