MySQL半同步复制原理与配置
一 、异步、同步和半同步复制概念
异步复制(Asynchronous replication),MySQL默认的复制是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。原理最简单,性能最好,但是主从之间数据不一致的概率很大。
全同步复制(Fully synchronous replication),指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication),介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制牺牲了一定的性能,提高了数据的安全性。
二、半同步复制原理
默认情况下,MySQL的主从复制是异步的,异步复制可以提供最佳的性能, 主库把binlog日志发送给从库,然后将结果返回给客户端,并不会验证从库是否接收完毕。这也就意味着有可能出现当主库或从库发生故障的时候,从库没有接收到主库发送过来的binlog日志,导致主库和从库的数据不一致,甚至在恢复时造成数据的丢失。
为了解决上述出现的问题,MySQL 5.5 引入了一种半同步复制模式。该模式可以确保从库接收完主库发送的binlog日志文件并写入到自己的中继日志relay log里,然后会给主库一个反馈,告诉主库已经接收完毕,这时主库才返回结果给客户端告知操作完成。当出现从库响应超时情况时,主库会暂时切换到异步复制模式,直到下一次同步没有超时转为半同步复制为止。(master的dump线程除了发送binlog数据到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降为普通的异步复制,直到异常修复)
三、半同步复制--MySQL5.7版
MySQL5.5半同步复制带来的新问题:
1)如果有故障发生,会切换为异步的复制。 那么从库出现数据不一致的几率会减少,并不是完全消失。
2)主机dump线程承担的工作变多了(发送binlog数据到slave,接收slave的ack。两者是串行的,dump线程必须等待slave返回ack之后才会传送下一个events事务。dump线程是整个半同步提高性能的瓶颈),这样显然会降低整个数据库的性能。
3)在MySQL 5.5和5.6使用after_commit的模式下, 如果slave没有收到事务,也就是还没有写入到relay log之前,网络出现异常或者不稳定,此时刚好master挂了,系统切换到从库,两边的数据就会出现不一致,slave会少一个事务的数据。
在此情况下,MySQL5.7的半同步复制技术升级为全新的Loss-less Semi-Synchronous Replication架构,新版本的semi sync增加了rpl_semi_sync_master_wait_point参数, 来控制半同步模式下主库在返回结果给会话之前提交事务的方式。
该参数有两个值:
1)AFTER_COMMIT(5.5,5.6默认值)
master将每个事务写入binlog之后,先提交事务,然后将binlog数据传递到slave并刷新到磁盘(relay log)。接着master等待slave反馈收到relay log,只有收到ack之后master才将commit OK结果反馈给客户端。如图一所示。
2)AFTER_SYNC(5.7默认值)
master将每个事务写入binlog,然后将binlog数据传递到slave并刷新到磁盘(relay log)。master等待slave反馈接收到relay log的ack之后,再提交事务并且返回commit OK结果给客户端。即使主库crash了,所有在主库上已经提交的事务都能保证已经同步到slave的relay log中。如图二所示。
MySQL5.7的半同步复制引入after_sync模式,主要是解决了after_commit导致的主库crash后主从之间数据不一致的问题。在引入after_sync模式后,所有提交的数据都已经被复制,故障切换时数据一致性将得到提升。(另外,在5.7版本的semi sync框架中,独立出一个ack collector thread,专门用于接收slave的反馈信息。这样master上有两个线程独立工作,可以同时发送binlog到slave,和接收slave的反馈)
四、半同步复制的安装
开启半同步复制,必须是MySQL5.5以上版本并且已经搭建好普通的主从异步复制。此处以MySQL5.5版本演示,如下所示:
1、安装半同步插件
#半同步功能主要是下面两个插件 [root@master ~]# ls -l /application/mysql/lib/plugin/ ...... -rwxr-xr-x 1 mysql mysql 173396 Sep 15 2017 semisync_master.so -rwxr-xr-x 1 mysql mysql 94066 Sep 15 2017 semisync_slave.so #分别在主从库加载上面两个插件 master: mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; slave: mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; #查看插件是否加载成功,有两种方法 1)mysql> show plugins; | rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL | 2)mysql> select plugin_name,plugin_status from information_schema.plugins where plugin_name like '%semi%'; +----------------------+---------------+ | plugin_name | plugin_status | +----------------------+---------------+ | rpl_semi_sync_master | ACTIVE | +----------------------+---------------+ 1 row in set (0.10 sec)
2、开启半同步复制
在安装完插件后,半同步复制默认是关闭的,这时需设置参数来开启半同步。
mater: mysql> set global rpl_semi_sync_master_enabled = 1; slave: mysql> set global rpl_semi_sync_slave_enabled = 1;
#以上的启动方式是在命令行操作,是临时生效的;永久生效需将如下设置写在配置文件中。 master: plugin-load = rpl_semi_sync_master=semisync_master.so #此项可以让plugin在任何时候都被mysql加载 rpl_semi_sync_master_enabled = 1 slave: plugin-load = rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled = 1
#在有的高可用架构下,master和slave需同时启动,以便在切换后能继续使用半同步复制 plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so" rpl-semi-sync-master-enabled = 1 rpl-semi-sync-slave-enabled = 1
3、重启slave上的IO线程使半同步生效
mysql> stop slave io_thread; mysql> start slave io_thread; #如果没有重启,则默认还是异步复制,重启后,slave会在master上注册为半同步复制的slave角色。 #查看半同步是否在运行 master: mysql> show status like 'Rpl_semi_sync_master_status'; +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_master_status | ON | +-----------------------------+-------+ slave: mysql> show status like 'Rpl_semi_sync_slave_status'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+
4、半同步复制测试
master: mysql> create database db; mysql> use db Database changed mysql> create table t1(id int); mysql> insert into t1 values(1); mysql> select * from t1; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) slave: mysql> select * from db.t1; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) #可以看到数据很快同步到了从库上,下面关闭io_thread测试 slave: mysql> stop slave io_thread; Query OK, 0 rows affected (0.00 sec) master: mysql> insert into t1 values(2); #此处有一个10s的超时等待时间,超时后转为异步插入 Query OK, 1 row affected (10.11 sec) mysql> show status like 'Rpl_semi_sync_master_status'; #半同步已失效 +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_master_status | OFF | +-----------------------------+-------+ slave: mysql> show status like 'Rpl_semi_sync_slave_status'; #从库的半同步也失效 +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +----------------------------+-------+ 1 row in set (0.01 sec) slave: mysql> start slave io_thread; #从库开启io线程 Query OK, 0 rows affected (0.00 sec) master: 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) slave: mysql> select * from db.t1; #从库上数据已同步 +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec)
5、其他说明
1)环境变量
mysql> show 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_no_slave | ON | +------------------------------------+-------+ 4 rows in set (0.00 sec)
rpl_semi_sync_master_enabled=ON 表示开启半同步复制
rpl_semi_sync_master_timeout=10000 单位为毫秒,即10秒超时,将切换为异步复制
rpl_semi_sync_master_wait_no_slave 表示是否允许master每个事务都要等待slave接收确认。默认为ON,每一个事务都会等待,如果slave crash后,当slave追赶上master的日志时,可以自动的切换为半同步方式。如果为OFF,则slave追赶上后,也不会采用半同步的方式复制了,需要手工配置。
rpl_semi_sync_master_trace_level=32 表示用于开启半同步复制时的调试级别,默认32
补充:
rpl_semi_sync_master_wait_for_slave_count MySQL 5.7.3引入的,该变量设置主库需要等待多少个slave应答,才能返回给客户端,默认为1。
2)状态变量
mysql> show status like '%semi%'; +--------------------------------------------+--------+ | Variable_name | Value | +--------------------------------------------+--------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 21014 | | Rpl_semi_sync_master_net_wait_time | 126089 | | Rpl_semi_sync_master_net_waits | 6 | | 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 | 2186 | | Rpl_semi_sync_master_tx_wait_time | 4373 | | Rpl_semi_sync_master_tx_waits | 2 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 4 | +--------------------------------------------+--------+ 14 rows in set (0.00 sec)
Rpl_semi_sync_master_status 标记master现在是否是半同步复制状态
Rpl_semi_sync_master_clients 记录支持半同步的slave的个数
Rpl_semi_sync_master_yes_tx master成功接收到slave的回复的次数
Rpl_semi_sync_master_no_tx master没有收到slave的回复而提交的次数