初识 MySQL 5.5 半同步复制
背景:
MySQL5.5之前的复制都是异步复制,在服务器崩溃时丢失事务是使用异步复制不可避免的结果。而5.5的一项新功能:半同步复制可以限制事务丢失的数量。半同步复制的原理是:提交事务的线程会被锁定,直到至少一个Slave收到这个事务,由于事务的事件在被提交到存储引擎之后才被发送到Slave上,所以事务的丢失数量可以下降到最多每线程一个。
安装、配置:半同步复制模式必须在主服务器和从服务器端同时启用,否则主服务器默认使用异步复制模式。
1,在Master上装插件:
install plugin rpl_semi_sync_master soname 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled=ON;
在my.cnf配置文件里,加入 rpl_semi_sync_master_enabled = 1
2,在Slave上装插件:
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled =ON;
在my.cnf配置文件里,加入rpl_semi_sync_slave_enabled = 1
3,和异步复制设置步骤一样。(帐号,Change。)这样半同步就算完成。
配置成功后,从的日志里面会有:
121210 7:28:43 [Note] Slave I/O thread: Start semi-sync replication to master 'repl@192.168.222.222:3306' in log 'mysql-bin.000001' at position 107
参数:
当所有的Slave崩溃,或则断开,甚至是只有Master没有Slave(不可能)。从而Slave接受不到Master的事务,导致M得不到S返回的确认信息。M会一直等待。这里有2个参数可以设置:
Master上:
rpl_semi_sync_master_enabled:表示在 master 上已经开启半同步复制模式。
rpl_semi_sync_master_timeout :为了防止半同步复制在没有收到S发出的确认发生堵塞,该可以设置一个计时器,用来设置超时,超过这个时间值没有收到信息,则切换到异步复制,执行操作。(默认为10000毫秒,等于10秒,这个参数动态可调,表示主库在某次事务中,如果等待时间超过10秒,那么则降级为异步复制模式,不再等待SLAVE从库。如果主库再次探测到,SLAVE从库恢复了,则会自动再次回到半同步复制模式。)
rpl_semi_sync_master_wait_no_slave :当一个事务被提交,但是Master没有Slave连接,这时M不可能收到任何确认信息,但M会在时间限制范围内继续等待。如果没有Slave链接,会切换到异步复制。(是否允许master每个事务提交后都要等待slave的接收确认信号。默认为on,每一个事务都会等待。如果为off,则slave追赶上后,也不会开启半同步复制模式,需要手工开启。)
Slave上:
rpl_recovery_rank:当 slave 从库宕机后,假如 Relay-Log 损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的 relay-log,并且重新从 MASTER 上获取日志,这样保证 relay-log 的完整。默认情况下该功能是关闭的, relay_log_recovery 的值设置为 1 时,将可在 slave从库上开启该功能。
mysql> show variables like '%rpl%'; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_recovery_rank | 0 | | 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 | +------------------------------------+-------+ 5 rows in set (0.00 sec)
mysql> show variables like 'rpl%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_recovery_rank | 0 | | rpl_semi_sync_slave_enabled | ON | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+ 3 rows in set (0.00 sec)
状态:
如何知道是半同步复制?
mysql> show global status like 'rpl_semi%'; +--------------------------------------------+--------+ | Variable_name | Value | +--------------------------------------------+--------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 40203 | | Rpl_semi_sync_master_net_wait_time | 361832 | | Rpl_semi_sync_master_net_waits | 9 | | 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 | 402 | | Rpl_semi_sync_master_tx_wait_time | 2011 | | Rpl_semi_sync_master_tx_waits | 5 | | 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)
mysql> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
解释:
Rpl_semi_sync_master_clients:说明支持和注册半同步复制的已链接Slave数。
Rpl_semi_sync_master_status:ON是活动状态(半同步),OFF是非活动状态(异步),要么是他没有启用或则已恢复到异步复制状态。用于指示主服务器使用的是异步复
制模式,还是半同步复制模式。
Rpl_semi_sync_master_yes_tx:成功提交数量。
Rpl_semi_sync_master_no_tx :不成功提交数量。
Rpl_semi_sync_slave_status :Slave上的半同步复制状态,ON表示已经被启用,OFF表示非活动状态。
测试:
复制正常的情况下,在主上插入一条会直接同步到从上去:
mysql> insert into test1 values(1,'a'); Query OK, 1 row affected (0.12 sec)
要是出现意外,从挂了,那么:
mysql> slave stop; Query OK, 0 rows affected (0.11 sec) mysql> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +----------------------------+-------+ 1 row in set (0.00 sec)
mysql> insert into test1 values(2,'aa'); Query OK, 1 row affected (10.12 sec) mysql> insert into test1 values(3,'aaa'); Query OK, 1 row affected (0.10 sec) mysql> show global status like 'rpl_semi%'; +--------------------------------------------+--------+ | Variable_name | Value | +--------------------------------------------+--------+ | Rpl_semi_sync_master_clients | 0 | | Rpl_semi_sync_master_net_avg_wait_time | 32934 | | Rpl_semi_sync_master_net_wait_time | 362277 | | Rpl_semi_sync_master_net_waits | 11 | | 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 | 402 | | Rpl_semi_sync_master_tx_wait_time | 2011 | | Rpl_semi_sync_master_tx_waits | 5 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 5 | +--------------------------------------------+--------+
上面可以看到:第一次插入,受rpl_semi_sync_master_timeout参数影响,超时等待了10s。第二次则直接可以执行,因为此时已经切换到了异步复制(Rpl_semi_sync_master_status = OFF )因为这2个插入语句是在非半同步情况下执行的,所以Rpl_semi_sync_master_no_tx 从1变成了3。
重新开启从,或则修复好了从崩溃的问题:
mysql> slave start; Query OK, 0 rows affected (0.01 sec) mysql> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
主的情况:主自动的开启了半同步。
| Rpl_semi_sync_master_status | ON |
注意:要是上面的情况频繁出现,会导致主上更新数据变得更慢。因为半同步复制和异步复制切换频繁,致使主频繁的等待接受从的信息而超时。
IO线程和SQL线程问题:
上面提到日志里面记录的信息:7:28:43 [Note] Slave I/O thread: Start semi-sync replication to master... ;是涉及到IO线程的,要是SQL线程挂了。那会怎么样?
主:
mysql> set sql_log_bin =0; Query OK, 0 rows affected (0.00 sec) mysql> create table test2 like test1; Query OK, 0 rows affected (0.14 sec) mysql> set sql_log_bin =1; Query OK, 0 rows affected (0.00 sec) mysql> drop table test2; Query OK, 0 rows affected (0.07 sec)
从: mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.200.245 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 784 Relay_Log_File: mysqld2-relay-bin.000010 Relay_Log_Pos: 634 Relay_Master_Log_File: mysql-bin.000002 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: 1051 Last_Error: Error 'Unknown table 'test2'' on query. Default database: 'test'. Query: 'DROP TABLE `test2` /* generated by server */' Skip_Counter: 0 Exec_Master_Log_Pos: 677 Relay_Log_Space: 1045 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: 1051 Last_SQL_Error: Error 'Unknown table 'test2'' on query. Default database: 'test'. Query: 'DROP TABLE `test2` /* generated by server */' Replicate_Ignore_Server_Ids: Master_Server_Id: 1 1 row in set (0.00 sec) mysql> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
上面可以看到:即使SQL线程报错了,半同步复制还是没有切换。表明半同步复制是跟 IO_THREAD 有直接关系,跟 SQL_THREAD 没关系。也就是说SLAVE 从库接收完二进制日志后给 master 主库一个确认,并不管relay-log 中继日志是否正确执行完。
总结:
好处:半同步复制可以有效的限制事务丢失的数量,更好的保证数据的安全和一致性;
坏处:更新、插入、删除的速度要比异步复制要慢,因为多了一个从返回信息给主的步骤。要是出现异常:网络问题或则数据库问题,半同步复制和异步复制就会来回切换,导致主库的更新、插入、删除操作会受到影响。