MySQL数据库复制的默认方式是
异步复制
,但是异步复制的不足之处就在于,当主库把event写入二进制日志之后,并不知道从库是否已经接收并应用了。在异步模式的复制,如果主库崩溃,很有可能在主库中已经提交的事务,并没有传到到任何一台从库机器上。在高可用集群架构下做主备切换,就会造成新的主库丢失数据的现象。
MySQL5.5版本之后引入了半同步复制功能,主从服务器必须同时安装半同步复制插件,才能开启该复制功能。在该功能下,确保从库接收完主库传递过来的binlog内容已经写入到自己的relay log里,才会通知主库上的等待线程,该操作完毕。如果等待超时,超过repl_semi_sync_master_timeout
参数设置的时间,则关闭半同步复制,并自动转换为异步复制
模式,直到至少有一台从库通知主库已经接收到binlog信息为止。
半同步复制提升了主从之间数据的一致性,让复制更加安全可靠。在MySQL5.7.2版本中增加了rpl_semi_sync_master_wait_point
参数,用来控制半同步模式下主库返回给session事务成功之前的事务提交方式。
该参数有两个值:
-
AFTER_COMMIT(MySQL5.6默认值)
主库将每个事务写入binlog,并传递给从库,刷新到中继日志中,同时主库提交事务,之后主库开始等待从库的反馈,只有收到从库的回复之后,master才将commit OK
的结果反馈给客户端。 -
AFTER_SYNC(MySQL5.7默认值)
主库将每个事务写入binlog,并传递给从库,刷新到中继日志中,主库开始等待从库的反馈,接收到从库的回复之后,再提交事务并且返回commit OK
结果给客户端。
可以通过
repl_semi_sync_master_wait_for_slave_count
参数来控制主库接收多少个从库写事务成功反馈,才返回成功给客户端。生产环境中使用半同步复制方式,当从库出现故障,等待超时的时间又很长,导致主库无法接收从库信息而无法正常写入时,可通过该参数剔除故障从库。
在AFTER_SYNC模式下,即使主库宕机,所有在主库上已经提交的事务都能保证已经同步到从库的中继日志中,不会丢失任何数据。
1. 半同步复制安装与配置
半同步复制的安装非常简单,它基于异步复制的基础上,安装半同步复制插件。
1.1 安装半同步复制插件
在master上:
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%';
+----------------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE |
+----------------------+---------------+
在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%';
+---------------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+---------------------+---------------+
| rpl_semi_sync_slave | ACTIVE |
+---------------------+---------------+
1.2 配置半同步复制
安装了半同步复制插件后,默认情况下会禁用它。必须在master和slave上都启用插件才能启用半同步复制。如果仅启用一侧,则复制将是异步的。
要控制是否已启用已安装的插件,请设置相应的系统变量。可以在运行时使用SET GLOBAL
或`在命令行或选项(my.cnf)文件中的服务器启动时设置这些变量。
在复制运行时,master可用的系统变量:
SET GLOBAL rpl_semi_sync_master_enabled = {0|1};
SET GLOBAL rpl_semi_sync_master_timeout = N;
在复制运行时,slave可用的系统变量
SET GLOBAL rpl_semi_sync_slave_enabled = {0|1};
对于 rpl_semi_sync_master_enabled
或 rpl_semi_sync_slave_enabled
,该值应为1以启用半同步复制,或者为0以禁用它。默认情况下,这些变量设置为0。
对于 rpl_semi_sync_master_timeout
,值N以毫秒为单位。默认值为10000(10秒)。
激活半同步复制,需要重启从库的I/O线程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
操作如下:
#master上
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
mysql> SET GLOBAL rpl_semi_sync_master_timeout = 60000;#该参数默认值是10s,应尽量调整大点,减小向异步复制切换以保证数据复制的安全性。
#要在服务器启动时,启动半同步复制,可以在my.cnf文件添加如下
[mysqld]
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=60000 # 60 second
#slave上
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
mysql> STOP SLAVE IO_THREAD;
mysql> START SLAVE IO_THREAD;
#要在服务器启动时,启动半同步复制,可以在my.cnf文件添加如下
[mysqld]
rpl_semi_sync_slave_enabled=1
2. 监控半同步
半同步复制功能的插件公开了几个系统和状态变量,可以检查这些变量以确定其配置和运行状态。
系统变量反映了如何配置半同步复制。要检查其值,请使用SHOW VARIABLES
:
#master
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 60000 |
| 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 |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)
#slave
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set (0.00 sec)
状态变量可以监视半同步复制的操作。要检查其值,请使用SHOW STATUS
:
#master
mysql> SHOW STATUS LIKE 'Rpl_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 | 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)
#slave
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.00 sec)
在master端:
Rpl_semi_sync_master_status
:此状态变量用来确定主服务器当前是使用异步还是半同步复制
Rpl_semi_sync_master_clients
:查看连接了多少个半同步slave
Rpl_semi_sync_master_no_tx
:代表没有成功接收slave事务提交回复的次数
Rpl_semi_sync_master_yes_tx
:代表成功接收slave事务提交回复的次数
Rpl_semi_sync_master_net_avg_wait_time
:主机等待slave回复的平均时间(以微秒为单位)。此变量已弃用,始终为0,将在以后的版本中删除。
Rpl_semi_sync_master_net_wait_time
:主机等待slave回复的总时间(以微秒为单位)。此变量已弃用,始终为0,将在以后的版本中删除。
Rpl_semi_sync_master_net_waits
:master等待slave回复的总次数。
Rpl_semi_sync_master_no_times
:主服务器关闭半同步复制的次数。
Rpl_semi_sync_master_timefunc_failures
:调用gettimeofday()
等时间函数时主服务器失败的次数。
Rpl_semi_sync_master_tx_avg_wait_time
:master等待每个事务的平均时间(以微秒为单位)。
Rpl_semi_sync_master_tx_wait_time
:master等待事务的总时间(以微秒为单位)。
Rpl_semi_sync_master_tx_waits
:主服务器等待事务的总次数。
Rpl_semi_sync_master_wait_pos_backtraverse
:master等待二进制坐标低于先前等待事件的事件的总次数。当事务开始等待回复的顺序与其二进制日志事件的写入顺序不同时,就会发生这种情况。
Rpl_semi_sync_master_wait_sessions
:当前正在等待slave回复的会话数。
在slave端:
Rpl_semi_sync_slave_status
:指示半同步复制当前是否可操作。
3. 半同步复制和异步复制模式的切换
半同步复制的原理是从库的I/O thread接收完主库的binlog,并把它写入relay中后,会给主库一个回馈。但如果主库等待从库的回馈时间超过repl_semi_sync_master_timeout
参数设置的时间,会自动切换为异步复制方式。
下面就利用rpl_semi_sync_master_timeout
超时设置,测试半同步复制与异步复制的切换。
#1. 查看主库rpl_semi_sync_master_timeout设置时间
root@localhost [(none)] 15:45:06> show variables like '%rpl_semi_sync_master_tim%';
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| rpl_semi_sync_master_timeout | 60000 |
+------------------------------+-------+
1 row in set (0.00 sec)
#2. 从库关掉I/O thread线程
root@localhost [(none)] 15:02:14>stop slave io_thread;
#3. 查看从库的半同步状态,为OFF
root@localhost [(none)] 15:46:49>show global status like '%semi%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF |
+----------------------------+-------+
1 row in set (0.00 sec)
#4. 查看主库的半同步复制状态
root@localhost [(none)] 15:58:03> 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)
#主库第一次插入耗时60s
root@localhost [test] 15:59:05> insert into z1 values(4,'python');
Query OK, 1 row affected (1 min 0.01 sec)
#主库第二次插入耗时0.01s
root@localhost [test] 16:00:25> insert into z1 values(5,'php');
Query OK, 1 row affected (0.01 sec)
# 主库查看半同步状态
root@localhost [test] 16:07:04> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 2 |
| Rpl_semi_sync_master_no_tx | 3 |
| Rpl_semi_sync_master_status | OFF |
| 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)
【说明】
第一次插入时,主库一直在等待从库的回复(60s),直到超过60s,主库再进行提交,且转换为异步复制。
第二次插入时,主从库已为异步复制,所以插入无需等待从库的反馈。