深刻MySQL复制(三):半同步复制

1.半同步复制

半同步复制官方手册:https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.htmlhtml

默认状况下,MySQL的复制是异步的,master将新生成的binlog发送给各slave后,无需等待slave的ack回复(slave将接收到的binlog写进relay log后才会回复ack),直接就认为此次DDL/DML成功了。mysql

半同步复制(semi-synchronous replication)是指master在将新生成的binlog发送给各slave时,只需等待一个(默认)slave返回的ack信息就返回成功。sql

MySQL 5.7对半同步复制做了大改进,新增了一个master线程。在MySQL 5.7之前,master上的binlog dump线程负责两件事:dump日志给slave的io_thread;接收来自slave的ack消息。它们是串行方式工做的。在MySQL 5.7中,新增了一个专门负责接受ack消息的线程ack collector thread。这样master上有两个线程独立工做,能够同时发送binlog到slave和接收slave的ack。数据库

还新增了几个变量,其中最重要的是 rpl_semi_sync_master_wait_point ,它使得MySQL半同步复制有两种工做模型。解释以下。安全

2.半同步复制的两种类型

从MySQL 5.7.2开始,MySQL支持两种类型的半同步复制。这两种类型由变量 rpl_semi_sync_master_wait_point (MySQL 5.7.2以前没有该变量)控制,它有两种值:AFTER_SYNC和AFTER_COMMIT。在MySQL 5.7.2以后,默认值为AFTER_SYNC,在此版本以前,等价的类型为AFTER_COMMIT。session

这个变量控制的是master什么时候提交、什么时候接收ack以及什么时候回复成功信息给客户端的时间点。并发

  1. AFTER_SYNC模式:master将新的事务写进binlog(buffer),而后发送给slave,再sync到本身的binlog file(disk)。以后才容许接收slave的ack回复,接收到ack以后才会提交事务,并返回成功信息给客户端。
  2. AFTER_COMMIT模式:master将新的事务写进binlog(buffer),而后发送给slave,再sync到本身的binlog file(disk),而后直接提交事务。以后才容许接收slave的ack回复,而后再返回成功信息给客户端。

画图理解就很清晰。(前提:已经设置了sync_binlog=1,不然binlog刷盘时间由操做系统决定)异步

再来分析下这两种模式的优缺点。socket

  • AFTER_SYNC
    • 对于全部客户端来讲,它们看到的数据是同样的,由于它们看到的数据都是在接收到slave的ack后提交后的数据。
    • 这种模式下,若是master忽然故障,不会丢失数据,由于全部成功的事务都已经写进slave的relay log中了,slave的数据是最新的。
  • AFTER_COMMIT
    • 不一样客户端看到的数据多是不同的。对于发起事务请求的那个客户端,它只有在master提交事务且收到slave的ack后才能看到提交的数据。但对于那些非本次事务的请求客户端,它们在master提交后就能看到提交后的数据,这时候master可能还没收到slave的ack。
    • 若是master收到ack回复前,slave和master都故障了,那么将丢失这个事务中的数据。

在MySQL 5.7.2以前,等价的模式是 AFTER_COMMIT ,在此版本以后,默认的模式为 AFTER_SYNC ,该模式能最大程度地保证数据安全性,且性能上并不比 AFTER_COMMIT 差。ide

3.半同步复制插件介绍

MySQL的半同步是经过加载google为MySQL提供的半同步插件 semisync_master.sosemisync_slave.so 来实现的。其中前者是master上须要安装的插件,后者是slave上须要安装的插件。

MySQL的插件位置默认存放在$basedir/lib/plugin目录下。例如,yum安装的mysql-server,插件目录为/usr/lib64/mysql/plugin。

[root@xuexi ~]# find / -type f -name "semisync*" 
/usr/lib64/mysql/plugin/debug/semisync_master.so
/usr/lib64/mysql/plugin/debug/semisync_slave.so
/usr/lib64/mysql/plugin/semisync_master.so
/usr/lib64/mysql/plugin/semisync_slave.so

由于要加载插件,因此应该保证须要加载插件的MySQL的全局变量 have_dynamic_loading 已经设置为YES(默认值就是YES),不然没法动态加载插件。

mysql> select @@global.have_dynamic_loading;
+-------------------------------+
| @@global.have_dynamic_loading |
+-------------------------------+
| YES                           |
+-------------------------------+

3.1 MySQL中安装插件的方式

安装插件有两种方式:1.在mysql环境中使用INSTALL PLUGIN语句临时安装;2.在配置文件中配置永久生效。

INSTALL安装插件的语法为:

Syntax:
INSTALL PLUGIN plugin_name SONAME 'shared_library_name'
UNINSTALL PLUGIN plugin_name

例如,使用INSTALL语句在master上安装 semisync_master.so 插件。

mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';

配置文件中加载插件的方式为:

[mysqld]
plugin-load='plugin_name=shared_library_name'

例如,配置文件中加载semisync_master.so插件。

[mysqld]
plugin-load="rpl_semi_sync_master=sermisync_master.so"

若是须要加载多个插件,则插件之间使用分号分隔。例如,在本节的slave1既是slave,又是master,须要同时安装两个半同步插件。

[mysqld]
plugin-load="rpl_semi_sync_master=semisync_master.so;rpl_sync_slave=semisync_slave.so"

安装插件后,应该使用show plugins来查看插件是否真的激活。

mysql> show plugins;
+----------------------+--------+-------------+--------------------+---------+
| Name                 | Status | Type        | Library            | License |
+----------------------+--------+-------------+--------------------+---------+
......
| rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL     |
+----------------------+--------+-------------+--------------------+---------+

或者查看information_schema.plugins表获取更详细的信息。

mysql> select * from information_schema.plugins where plugin_name like "%semi%"\G
*************************** 1. row ***************************
           PLUGIN_NAME: rpl_semi_sync_master
        PLUGIN_VERSION: 1.0
         PLUGIN_STATUS: ACTIVE
           PLUGIN_TYPE: REPLICATION
   PLUGIN_TYPE_VERSION: 4.0
        PLUGIN_LIBRARY: semisync_master.so
PLUGIN_LIBRARY_VERSION: 1.7
         PLUGIN_AUTHOR: He Zhenxing
    PLUGIN_DESCRIPTION: Semi-synchronous replication master
        PLUGIN_LICENSE: GPL
           LOAD_OPTION: ON
1 row in set (0.00 sec)

插件装载完成后,半同步功能还未开启,须要手动设置它们启动,或者写入配置文件永久生效。

# 开启master的半同步
mysql> set @@global.rpl_semi_sync_master_enabled=1;

# 开启slave半同步
mysql> set @@globale.rpl_semi_sync_slave_enabled=1;

或者配合插件加载选项一块儿写进配置文件永久开启半同步功能。

[mysqld]
rpl_semi_sync_master_enabled=1

[mysqld]
rpl_semi_sync_slave_enabled=1

3.2 半同步插件相关的变量

安装了 semisync_master.sosemisync_slave.so 后,这两个插件分别提供了几个变量。

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               | OFF        |
| rpl_semi_sync_slave_trace_level           | 32         |
+-------------------------------------------+------------+

下面还多给了两个和半同步相关的状态变量的解释,能够经过show status like %semi%;查看它们。

  1. master相关的变量:
    • ①.Rpl_semi_sync_master_clients:(状态变量)master所拥有的半同步复制slave的主机数量。
    • ②.Rpl_semi_sync_master_status :(状态变量)master当前是否以半同步复制状态工做(ON),OFF表示降级为了异步复制。
    • ③.rpl_semi_sync_master_enabled:master上是否启用了半同步复制。
    • ④.rpl_semi_sync_master_timeout:等待slave的ack回复的超时时间,默认为10秒。
    • ⑤.rpl_semi_sync_master_trace_level:半同步复制时master的调试级别。
    • ⑥.rpl_semi_sync_master_wait_for_slave_count:master在超时时间内须要收到多少个ack回复才认为这次DML成功,不然就降级为异步复制。该变量在MySQL5.7.3才提供,在此以前的版本都默认为收到1个ack则确认成功,且不可更改。MySQL 5.7.3以后该变量的默认值也是1。
    • ⑦.rpl_semi_sync_master_wait_no_slave:值为ON(默认)或者OFF。ON表示master在超时时间内若是未收到指定数量的ack消息,则会一直等待下去直到收满ack,即一直采用半同步复制方式,不会降级;OFF表示若是在超时时间内未收到指定数量的ack,则超时时间一过当即降级为异步复制。

      更官方的解释是:当设置为ON时,即便状态变量Rpl_semi_sync_master_clients中的值小于rpl_semi_sync_master_wait_for_slave_count,Rpl_semi_sync_master_status依旧为ON;当设置为OFF时,若是clients的值小于count的值,则Rpl_semi_sync_master_status当即变为OFF。通俗地讲,就是在超时时间内,若是slave宕机的数量超过了应该要收到的ack数量,master是否降级为异步复制。

      该变量在MySQL 5.7.3以前彷佛没有效果,由于默认设置为ON时,超时时间内收不到任何ack时仍然会降级为异步复制。

    • ⑧.rpl_semi_sync_master_wait_point:控制master上commit、接收ack、返回消息给客户端的时间点。值为 AFTER_SYNCAFTER_COMMIT ,该选项是MySQL5.7.2后引入的,默认值为 AFTER_SYNC ,在此版本以前,等价于使用了 AFTER_COMMIT 模式。关于这两种模式,见前文对两种半同步类型的分析。

  2. slave相关的变量:
    • ①.rpl_semi_sync_slave_enabled:slave是否开启半同步复制。
    • ②.rpl_semi_sync_slave_trace_level:slave的调试级别。

4.配置半同步复制

须要注意的是,"半同步"是同步/异步类型的一种状况,既能够实现半同步的传统复制,也能够实现半同步的GTID复制。其实半同步复制是基于异步复制的,它是在异步复制的基础上经过加载半同步插件的形式来实现半同步性的。

此处以全新的环境进行配置,方便各位道友"依葫芦画瓢"。

本文实现以下拓扑图所示的半同步传统复制。若是要实现半同步GTID复制,也只是在gtid复制的基础上改改配置文件而已。

具体环境:

称呼 主机IP MySQL版本 OS 角色(master/slave) 数据库状态
master 192.168.100.21 MySQL 5.7.22 CentOS 7.2 master 全新实例
salve1 192.168.100.22 MySQL 5.7.22 CentOS 7.2 semi_slave for master
semi_master for other slaves
全新实例
slave2 192.168.100.23 MySQL 5.7.22 CentOS 7.2 semi_slave for slave1 全新实例
slave3 192.168.100.24 MySQL 5.7.22 CentOS 7.2 semi_slave for slave1 全新实例

由于都是全新的实例环境,因此无需考虑基准数据和binlog坐标的问题。若是开始测试前,已经在master上作了一些操做,或者建立了一些新数据,那么请将master上的数据恢复到各slave上,并获取master binlog的坐标,具体操做方法可参见前文:将slave恢复到master指定的坐标

4.1 半同步复制的配置文件

首先提供各MySQL Server的配置文件。

如下是master的配置文件。

[mysqld]
datadir=/data
socket=/data/mysql.sock
log-error=/data/error.log
pid-file=/data/mysqld.pid
log-bin=/data/master-bin
sync-binlog=1
server-id=100
plugin-load="rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled=1

如下是slave1的配置文件,注意slave1同时还充当着slave2和slave3的master的角色。

[mysqld]
datadir=/data
socket=/data/mysql.sock
log-error=/data/error.log
pid-file=/data/mysqld.pid
log-bin=/data/master-bin
sync-binlog=1
server-id=110
relay-log=/data/slave-bin
log-slave-updates
plugin-load="rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled=1
rpl_semi_sync_master_enabled=1

如下是slave2和slave3的配置文件,它们配置文件除了server-id外都一致。

[mysqld]
datadir=/data
socket=/data/mysql.sock
log-error=/data/error.log
pid-file=/data/mysqld.pid
server-id=120      # slave3的server-id=130
relay-log=/data/slave-bin
plugin-load="rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled=1
read-only=on

4.2 启动复制线程

如今master上建立一个专门用于复制的用户。

mysql> create user repl@'192.168.100.%' identified by 'P@ssword1!';
mysql> grant replication slave on *.* to repl@'192.168.100.%';

由于master和全部的slave都是全新的实例,因此slave上指定的binlog坐标能够从任意位置开始。不过刚才master上建立了一个用户,也会写binlog,因此建议仍是从master的第一个binlog的position=4开始。

如下是slave1上的change master to参数:

mysql> change master to 
            master_host='192.168.100.21',
            master_port=3306,
            master_user='repl',
            master_password='P@ssword1!',
            master_log_file='master-bin.000001',
            master_log_pos=4;

如下是slave2和slave3的change master to参数:

mysql> change master to 
            master_host='192.168.100.22',
            master_port=3306,
            master_user='repl',
            master_password='P@ssword1!',
            master_log_file='master-bin.000001',
            master_log_pos=4;

启动各slave上的两个SQL线程。

mysql> start slave;

一切就绪后,剩下的事情就是测试。在master上对数据作一番修改,而后查看是否会同步到slave一、slave二、slave3上。

5.半同步复制的状态信息

首先是semisync相关的可修改变量,这几个变量在前文已经解释过了。

例如如下是开启了半同步复制后的master上的semisync相关变量。

mysql> show global variables like "%semi%";
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| 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 |
+-------------------------------------------+------------+

关于半同步复制,还有几个状态变量很重要。

例如,如下是master上关于semi_sync的状态变量信息。

mysql> show status like "%semi%";
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |  # 注意行1
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 5     |
| Rpl_semi_sync_master_no_times              | 1     |
| Rpl_semi_sync_master_no_tx                 | 1     |
| Rpl_semi_sync_master_status                | ON    |  # 注意行2
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 384   |
| Rpl_semi_sync_master_tx_wait_time          | 1537  |
| Rpl_semi_sync_master_tx_waits              | 4     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 4     |
+--------------------------------------------+-------+

除了上面标注"注意行"的变量,其余都无需关注,并且其中有一些是废弃了的状态变量。

Rpl_semi_sync_master_clients是该master所链接到的slave数量。

Rpl_semi_sync_master_status是该master的半同步复制功能是否开启。在有些时候半同步复制会降级为异步复制,这时它的值为OFF。

如下是slave1上关于semi_sync的状态变量信息。

mysql>  show status like "%semi%";
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 2     |  # 注意行1
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 8     |
| Rpl_semi_sync_master_no_times              | 2     |
| Rpl_semi_sync_master_no_tx                 | 4     |
| Rpl_semi_sync_master_status                | ON    |  # 注意行2
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 399   |
| Rpl_semi_sync_master_tx_wait_time          | 1199  |
| Rpl_semi_sync_master_tx_waits              | 3     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 3     |
| Rpl_semi_sync_slave_status                 | ON    |  # 注意行3
+--------------------------------------------+-------+

此外,从MySQL的错误日志、show slave status也能获取到一些半同步复制的状态信息。下一节测试半同步复制再说明。

6.测试半同步复制(等待、降级问题)

前面已经搭建好了下面的半同步复制结构。

|------> slave2
                master ---> slave1 ---
                                     |------> slave3

下面来测试半同步复制降级为异步复制的问题,借此来观察一些semisync的状态变化。

首先,只停掉slave2或slave3中其中一个io线程的话,slave1是不会出现降级的,由于默认的半同步复制只需等待一个ack回复便可返回成功信息。

若是同时停掉slave2和slave3的io线程,当master更新数据后,slave1在10秒(默认)以后将降级为异步复制。以下:

在slave2和slave3上执行:

mysql> stop slave io_thread;

在master上执行:

create database test1;
create table test1.t(id int);
insert into test1.t values(33);

在slave1上查看(在上面的步骤以后的10秒内查看):

mysql> show status like "%semi%";
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |  # 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             | 8     |
| Rpl_semi_sync_master_no_times              | 2     |
| Rpl_semi_sync_master_no_tx                 | 4     |
| Rpl_semi_sync_master_status                | ON    |  # status=ON
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 399   |
| Rpl_semi_sync_master_tx_wait_time          | 1199  |
| Rpl_semi_sync_master_tx_waits              | 3     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 1     |
| Rpl_semi_sync_master_yes_tx                | 3     |
| Rpl_semi_sync_slave_status                 | ON    |
+--------------------------------------------+-------+

能够看到在这一小段时间内,slave1仍是半同步复制。此时用show slave status查看slave1。

# slave1上执行
mysql> show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.100.21
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000004
          Read_Master_Log_Pos: 1762
               Relay_Log_File: slave-bin.000005
                Relay_Log_Pos: 1729
        Relay_Master_Log_File: master-bin.000004
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
................................................
      Slave_SQL_Running_State: Waiting for semi-sync ACK from slave
................................................

此时slave的SQL线程状态是Waiting for semi-sync ACK from slave

但10秒以后再查看。

mysql> show 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             | 8     |
| Rpl_semi_sync_master_no_times              | 3     |
| Rpl_semi_sync_master_no_tx                 | 5     |
| Rpl_semi_sync_master_status                | OFF   |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 399   |
| Rpl_semi_sync_master_tx_wait_time          | 1199  |
| Rpl_semi_sync_master_tx_waits              | 3     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 3     |
| Rpl_semi_sync_slave_status                 | ON    |
+--------------------------------------------+-------+

发现slave1已经关闭半同步功能了,也就是说降级为异步复制了。

此时查看slave1的错误日志。

2018-06-11T03:43:21.765384Z 4 [Warning] Timeout waiting for reply of binlog (file: master-bin.000001, pos: 2535), semi-sync up to file master-bin.000001, position 2292.
2018-06-11T03:43:21.765453Z 4 [Note] Semi-sync replication switched OFF.

它先记录了当前slave2/slave3中已经同步到slave1的哪一个位置。而后将Semi-sync复制切换为OFF状态,即降级为异步复制。

在下次slave2或slave3启动IO线程时,slave1将自动切换回半同步复制,并发送那些未被复制的binlog。

相关文章
相关标签/搜索