MySQL5.5之前,MySQL的复制是异步操作,主库和从库的数据之间存在一定的延迟。这样存在一定的隐患:当主库上写入一个事务并
交成功,而从库尚未得到主库推送的Binlog日志时,主库宕机了,例如主库可能因磁盘损坏、内存故障等造成主库上该事务Binlog丢失,此时从库就可能损失这个事务,从而造成主从不一致。
为了解决这个问题,MySQL5.5引入了半同步复制机制。在MySQL5.5之前的异步复制时,主库完成了Commit提交操作之后,在主库写
Binlog日志后即可成功返回客户端,无需等待Binlog日志传送给从库。
而半同步复制时,为了保证主库上的每一个Binlog事务都能够被可靠的复制到从库上,主库在每次事务提交时,并不及时反馈给前
台应用用户,而是等待其中一个从库也接收到Binlog事务并成功写入中继日志后,主库才返回Commit操作成功给客户端。半同步复制保证
了事务成功提交后,至少有两份日志记录,一份在主库的Binlog日志上,另一份在至少一台从库的中继日志Relay Log上,从而更进一步
保证了数据的完整性。
半同步复制模式下,如果主库在事务提交前的任何一个环节故障,则事务提交不成功,从库上也没收到事务对应的Binlog日志,所
以主从数据是一致的;假如在主库传送Binlog日志到从库过程中,从库宕机或网络故障,导致Binlog并没有及时地传送到从库上,此时
主库的事务会等待一段时间(时间长短由rpl_semi_sync_master_timeout设置的秒数决定),如果Binlog在这段时间内无法成功推送到从
库上,则MySQL自动调整复制为异步模式,事务正常返回提交结果给客户端
半同步很大程度上取决于主从库之间的网络状况,往返时延RTT越小决定了从库实时性越好。通俗的说主从之间的网络越快从库越实
时。
半同步模式是作为MySQL5.5的一个插件来实现的,主库和重开使用不同的插件,安装比较简单,在上一节异步复制的基础上,安装
半同步复制插件即可。
(1)、首先,判断MySQL服务器是否支持动态增加插件:
MariaDB [(none)]> select @@have_dynamic_loading;
+------------------------+
| @@have_dynamic_loading |
+------------------------+
| YES |
+------------------------+
1 row in set (0.01 sec)
(2)、确认支持增加插件后,检查MySQL的安装目录下是否存在插件,一般默认在$MYSQL_HOME/lib/plugin目录下存在主库插件
semisync_master.so和从库插件semisync_slave.so
[[email protected] ~]# ls /usr/local/mysql/lib/plugin/semisync_*
-rwxr-xr-x 1 root root 510355 Jun 13 09:44 /usr/local/mysql/lib/plugin/semisync_master.so
-rwxr-xr-x 1 root root 427031 Jun 13 09:45 /usr/local/mysql/lib/plugin/semisync_slave.so
在主库上安装插件semisync_master.so:
MariaDB [(none)]> install plugin rpl_semi_sync_master soname ‘semisync_master.so‘;
Query OK, 0 rows affected (0.07 sec)
从库上则安装semisync_slave.so:
MariaDB [(none)]> install plugin rpl_semi_sync_slave soname ‘semisync_slave.so‘;
Query OK, 0 rows affected (0.08 sec)
安装完成后,从plugin表中看到刚才安装的插件:
MariaDB [(none)]> select * from mysql.plugin;
+----------------------+--------------------+
| name | dl |
+----------------------+--------------------+
| rpl_semi_sync_master | semisync_master.so |
+----------------------+--------------------+
1 row in set (0.00 sec)
MariaDB [(none)]> select * from mysql.plugin;
+---------------------+-------------------+
| name | dl |
+---------------------+-------------------+
| rpl_semi_sync_slave | semisync_slave.so |
+---------------------+-------------------+
1 row in set (0.00 sec)
也就是说,安装完成后,MySQL会在系统表plugin中记录刚才安装的插件,下次重启后会自动加载插件。
(3)、需要分别在主库和从库上配置参数打开半同步 semi-sync,默认半同步设置是不打开的,
主库上配置全局参数:
MariaDB [(none)]> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> set global rpl_semi_sync_master_timeout=30000;
Query OK, 0 rows affected (0.00 sec)
MariaDB [pancou]> show variables like ‘%rpl_semi%‘;
+------------------------------------+-------+
| Variable_name | Value |
+------------------------------------+-------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 30000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_no_slave | ON |
+------------------------------------+-------+
4 rows in set (0.00 sec)
从库上一样的配置参数:
MariaDB [(none)]> set global rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.00 sec)
MariaDB [pancou]> 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)
注意,由于之前配置的复制是异步复制,所以要重启一下从库上的I/O线程(如果是全新配置的半同步复制则不需要):
MariaDB [(none)]> stop slave io_thread;start slave io_thread;
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
至此半同步配置完毕,下面可以来验证一下。主库上通过show status命令能够看到当前半同步复制的一些状态值:
MariaDB [(none)]> show status like ‘%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)
着重关注以下3个状态值:
Rpl_semi_sync_master_status:值为ON,表示半同步复制目前处于打开状态。
Rpl_semi_sync_master_yes_tx:值为0,表示主库当前尚未有任何一个事务时通过半同步复制到从库。
Rpl_semi_sync_master_no_tx: 值为0,表示当前有0个事务不是通过半同步模式下从库及时响应的(记住只给值,后面有对比)。
执行一个事务,再检查一下:
MariaDB [pancou]> create table emp(emp_id smallint,emp_name varchar(10));
Query OK, 0 rows affected (0.10 sec)
MariaDB [pancou]> insert into emp values(1,‘tom‘),(2,‘jerry‘);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 1496 |
| Rpl_semi_sync_master_net_wait_time | 2992 |
| Rpl_semi_sync_master_net_waits | 2 |
| 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 | 1203 |
| Rpl_semi_sync_master_tx_wait_time | 2407 |
| 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 | 2 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
此时会发现Rpl_semi_sync_master_yes_tx值变为2,即刚才的create 和 insert事务通过半同步复制到从库上了,
Rpl_semi_sync_master_yes_tx 基数增加到2,到从库上确认一下:
MariaDB [pancou]> select * from emp;
+--------+----------+
| emp_id | emp_name |
+--------+----------+
| 1 | tom |
| 2 | jerry |
+--------+----------+
2 rows in set (0.00 sec)
再尝试一下网络异常的场景下,主库等待rpl_semi_sync_master_timeout毫秒超时后,自动转成异步复制的场景。
(1)、首先,在主库上确认半同步会等待30秒超时;
MariaDB [pancou]> show variables like ‘%semi%time%‘;
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| rpl_semi_sync_master_timeout | 30000 |
+------------------------------+-------+
1 row in set (0.00 sec)
(2)、在主库上通过iptables命令模拟从库宕机或网络故障:
第二天
MariaDB [pancou]> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.00 sec)
show variables like ‘rpl_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.01 sec)
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+-------+
| 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 | 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)
MariaDB [pancou]> delete from emp where emp_id=2;
Query OK, 1 row affected (0.01 sec)
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 1494 |
| Rpl_semi_sync_master_net_wait_time | 1494 |
| Rpl_semi_sync_master_net_waits | 1 |
| 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 | 1 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
在从库上
iptables -I INPUT -s 192.168.110.128 -j DROP
(3)、在主库上执行一个事务并提交(默认提交即可),主库上的提交操作会被阻塞30秒:
MariaDB [pancou]> insert into emp values(2,‘jerry‘);
---等待
新打开一个主库的线程
MariaDB [(none)]> show processlist\G
*************************** 1. row ***************************
Id: 7
User: root
Host: localhost
db: NULL
Command: Query
Time: 0
State: init
Info: show processlist
Progress: 0.000
*************************** 2. row ***************************
Id: 10
User: root
Host: localhost
db: pancou
Command: Query
Time: 4
State: Waiting for semi-sync ACK from slave
Info: insert into emp values(2,‘jerry‘)
Progress: 0.000
*************************** 3. row ***************************
Id: 11
User: repl_user
Host: 192.168.110.130:52165
db: NULL
Command: Binlog Dump
Time: 2037
State: Reading from net
Info: NULL
Progress: 0.000
3 rows in set (0.00 sec)
MariaDB [(none)]> show processlist\G
*************************** 1. row ***************************
Id: 6
User: root
Host: localhost
db: pancou
Command: Sleep
Time: 248
State:
Info: NULL
Progress: 0.000
*************************** 2. row ***************************
Id: 7
User: root
Host: localhost
db: NULL
Command: Query
Time: 0
State: init
Info: show processlist
Progress: 0.000
2 rows in set (0.00 sec)
(4)、主库上等待30秒后
Query OK, 1 row affected (10.00 sec)
MariaDB [pancou]> insert into emp values(2,‘jerry‘);
Query OK, 1 row affected (0.01 sec)
检查半同步的状态值
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+----------+
| Variable_name | Value |
+--------------------------------------------+----------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 15016390 |
| Rpl_semi_sync_master_net_wait_time | 30032780 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 2 |
| 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 | 1 |
+--------------------------------------------+----------+
14 rows in set (0.00 sec)
Rpl_semi_sync_master_status | OFF
Rpl_semi_sync_master_yes_tx | 1 刚才并没有通过半同步复制完成,所以半同步并没有累加
Rpl_semi_sync_master_no_tx | 2
继续之前的场景下从库正常连接上主机之后,确认是否会自动切换回半同步复制模式。
iptables -D INPUT 1
检查从库上的状态:
MariaDB [pancou]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.110.128
Master_User: repl_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 1940
Relay_Log_File: www-relay-bin.000011
Relay_Log_Pos: 1024
Relay_Master_Log_File: mysql-bin.000005
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
(2)、检查半同步复制状态值
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+----------+
| Variable_name | Value |
+--------------------------------------------+----------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 10019692 |
| Rpl_semi_sync_master_net_wait_time | 30059077 |
| Rpl_semi_sync_master_net_waits | 3 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 3 |
| 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 | 1 |
+--------------------------------------------+----------+
14 rows in set (0.00 sec)
Rpl_semi_sync_master_status 值自动由OFF变为 | ON ,说明主库检测到正常以后主库到从库的复制方式自动切换为
半同步方式
(3)、在主库上做一个测试
MariaDB [pancou]> insert into emp values(3,‘bob‘);
Query OK, 1 row affected (0.01 sec)
MariaDB [pancou]> show status like ‘%semi_sync%‘;
+--------------------------------------------+----------+
| Variable_name | Value |
+--------------------------------------------+----------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 7515331 |
| Rpl_semi_sync_master_net_wait_time | 30061326 |
| Rpl_semi_sync_master_net_waits | 4 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 3 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 742 |
| Rpl_semi_sync_master_tx_wait_time | 742 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+----------+
14 rows in set (0.01 sec)
提交一个事务后,Rpl_semi_sync_master_yes_tx 由1变为 2,确认刚才事务是半同步复制。
从半同步复制的流程会发现,半同步复制的 “半” 就体现在:虽然主库和从库的Binlog日志是同步的,但是主库并不等待从库应用这部分
日志就返回提交结果,这部分操作是异步的,从库的数据并不是和主库实时同步的,所以只能称之为半同步而不是完全的实时同步。