默认情况下,MySQL 5.5/5.6/5.7和MariaDB 10.0/10.1的复制是异步的,异步复制可以提供最佳性能,主库把binlog日志发送给从库,这一动作就结束了,并不会验证从库是否接收完毕,但这同时也带来了很高的风险,这就意味着当主服务器或从服务器发生故障时,有可能从库没有接收到主库发过来的binlog日志,造成主从不一致,甚至恢复是数据丢失。
为了解决上面问题,MySQL 5.5引入了半同步复制(Semi Replication)模式,该模式可以确保从服务器接收完主服务器发送的binlog日志文件并写入自己的中继日志(Relay Log)里,然后会给主服务器一个反馈,告诉对方已经接收完毕,这时主库线程才返回给当前session告知操作完成,当出现超市情况时,源主服务器会暂时切换到异步复制模式,直到至少有一台设置为半同步复制模式的从服务器及时收到信息为止。
所以,半同步在一定程度上可保证提交的事务已经传递给了至少一个备库,因此,半同步复制与异步复制相比,进一步提高了数据的完整性。
配置半同步复制:
半同步复制插件在/usr/local/mysql/lib/plugin下,这里环境已经配置好了主从复制。
[root@mysql-A plugin]# ll -h semisync_* -rwxr-xr-x 1 mysql mysql 504K Aug 4 15:58 semisync_master.so -rwxr-xr-x 1 mysql mysql 419K Aug 4 15:58 semisync_slave.so
首先安装插件,并开启半同步复制,主库如下:
[root@mysql-A ~]# mysql MariaDB [(none)]> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; Query OK, 0 rows affected (5.25 sec)
从库如下:
[root@mysql-B ~]# mysql
MariaDB [(none)]> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.11 sec)
检查插件是否加载成功:
MariaDB [(none)]> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; +-------------------------+-------------------+ | PLUGIN_NAME | PLUGIN_STATUS | +-------------------------+-------------------+ | rpl_semi_sync_master | ACTIVE | +-------------------------+-------------------+
在初次加载插件后,MySQL会将该插件记录到系统表mysql.plugin中,下次启动时系统则会自动加载该插件,无需再次执行上面命令。
启用半同步复制,在安装完插件后,半同步复制默认是关闭的,这时需设置参数来开启半同步
主: MariaDB [(none)]> SET GLOBAL rpl_semi_sync_master_enabled = 1; Query OK, 0 rows affected (0.00 sec) 备: MariaDB [(none)]> SET GLOBAL rpl_semi_sync_slave_enabled = 1; Query OK, 0 rows affected (0.00 sec)
这里的开启只是在实例中开启,下次重启将失效,所以需要在my.cnf中配置:
主:
plugin-load=rpl_semi_sync_master=semisync_master.so rpl_semi_sync_master_enabled=1
备:
plugin-load=rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled=1
当然还有其他参数可以配置:
主库:
rpl_semi_sync_master_enabled=ON:表示在master上已经开启了半同步复制模式。 rpl_semi_sync_master_timeout=10000:当半同步复制发生超时时(由rpl_semi_sync_master_timeout参数控制,单位是毫秒,默认为10000,即10s),会暂时关闭半同步复制,转而使用异步复制。当master dump线程发送完一个事务的所有事件之后,如果在rpl_semi_sync_master_timeout内,收到了从库的响应,则主从又重新恢复为半同步复制。 rpl_semi_sync_master_wait_no_slave:表示是否允许master每个事务提交后都要等待slave的接收确认信号。默认是on,即每一个事务都会等待,如果为off,则slave追赶上后,也不会开启半同步复制模式,需要手动开启。 rpl_semi_sync_master_trace_level=32:表示用于开启半同步复制模式时的调试级别,默认是32。
从库:
rpl_semo_sync_slave_enabled=ON:表示在slave上已经开启半同步复制模式。 rpl_semi_sync_master_trace_level=32:表示用于开启半同步复制模式时的调试级别,默认是32。
在有的高可用架构下(MHA)等,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
重启从库上IO进程:
MariaDB [(none)]> STOP SLAVE IO_THREAD; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> START SLAVE IO_THREAD; Query OK, 0 rows affected (0.00 sec)
此时主库的错误日志可以看到如下内容:
180117 2:33:39 [Note] Semi-sync replication initialized for transactions. 180117 2:33:39 [Note] Semi-sync replication enabled on the master. 180117 2:35:49 [Note] Stop asynchronous binlog_dump to slave (server_id: 2) 180117 2:35:49 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(mysql-bin.000007, 753)
查看半同步状态:
主:
MariaDB [(none)]> show status like 'Rpl_semi_sync_master_status'; +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_master_status | ON | +-----------------------------+-------+ 1 row in set (0.00 sec)
备:
MariaDB [(none)]> 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半同步复制搭建完毕。
测试一:
当slave从库的io_thread线程将binlog日志接收完毕时,要给master主库一个确认信号,如果rpl_semi_sync_master_timeout=10000(10秒)超过10秒未收到slave从库的接收信号,那么就会自动转换为传统的异步复制模式:
从库关闭io_thead线程:
MariaDB [(none)]> stop slave io_thread; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> show status like '%semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +----------------------------+-------+ 1 row in set (0.01 sec)
主库创建一张表:
MariaDB [abbott]> create table t1(id int); Query OK, 0 rows affected (10.07 sec) MariaDB [abbott]> show status like '%semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 76 | | Rpl_semi_sync_master_net_wait_time | 76 | | Rpl_semi_sync_master_net_waits | 1 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | 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)
可以看到这个语句执行了10秒钟才成功,执行完成之后变成了传统主从复制。再次启动io_thread线程之后半同步复制正常。
以上值得关注的参数:
rpl_semi_sync_master_status:用于指示主服务器是使用异步复制模式,还是半同步复制模式。
rpl_semi_sync_master_clients:用于显示有多少个从服务器配置成了半同步复制模式。
rpl_semi_sync_master_yes_tx:用于显示从服务器确认的成功提交数量。
rpl_semi_sync_master_no_tx:用于显示从服务器确认的不成功提交数量。
测试二:模拟同步报错
试着这样操作:在slave从库上执行drop database abbott,删除一个库,然后在master主机上再次执行drop database abbott,这时同步报错:
MariaDB [(none)]> drop database abbott; Query OK, 2 rows affected (0.19 sec) MariaDB [(none)]> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.165.135 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000007 Read_Master_Log_Pos: 1002 Relay_Log_File: mysql-bin.000004 Relay_Log_Pos: 661 Relay_Master_Log_File: mysql-bin.000007 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1008 Last_Error: Error 'Can't drop database 'abbott'; database doesn't exist' on query. Default database: 'abbott'. Query: 'drop database abbott' Skip_Counter: 0 Exec_Master_Log_Pos: 879 Relay_Log_Space: 2231 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: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 1008 Last_SQL_Error: Error 'Can't drop database 'abbott'; database doesn't exist' on query. Default database: 'abbott'. Query: 'drop database abbott' Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_SSL_Crl: Master_SSL_Crlpath: Using_Gtid: No Gtid_IO_Pos: 1 row in set (0.00 sec)
查看同步状态:
MariaDB [(none)]> show status like '%semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+
从上面信息可以看出,没有转化为异步复制模式,仍然是半同步复制模式,可以半同步复制跟IO_THREAD是直接有关系,但跟SQL_THREAD无关。也就是说,slave从库接收完二进制日志后会给master主库一个确认信号,但它并不会管中继日志是否执行完。