深入理解Mysql复制机制
在以前的文章中,探讨了Mysql主从复制的部署实施,但这种复制模式存在以下问题:
- 主库宕机后,数据可能丢失
- 从库只有一个sql Thread,主库写压力大,复制很可能延时
由于Mysql的主从复制都是单线程的操作,当主库的TPS并发较高时,产生的DDL数量超过从库一个sql线程所能承受的范围,那么延时就产生了,还有就是可能与slave的大型query语句产生了锁等待。
可以使用多台slave来分摊读请求,再从这些slave中取一台专用的服务器,只作为备份用,不进行其他任何操作,就能相对最大限度地达到实时
的要求了,同时也可以采用其他的复制策略来实施。
MySQL复制支持多种不同的复制策略,包括异步、全同步、半同步同步。
- 异步复制:Master不需要等待Slave应答就可以提交。
- 同步复制:Master要等待所有Slave应答之后才会提交(MySql对DB操作的提交通常是先对操作事件进行二进制日志文件写入然后再进行提交)。
- 半同步复制:Master等待至少一个Slave应答就可以提交。
# 异步复制
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
# 同步复制
同步复制指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
# 半同步复制
一般情况下,异步复制就已经足够应付了,但由于是异步复制,备库极有可能是落后于主库,特别是极端情况下,我们无法保证主备数据的一致性(即使我们观察到Seconds Behind Master 这个值为0)。
MySQL从5.5开始推出了半同步复制。相比异步复制,半同步复制提高了数据完整性,因为很明确知道,在一个事务提交成功之后,这个事务就至少会存在于两个地方。即在master的dump线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dump线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复后又会自动变为半同步复制。
# 半同步复制原理
- 从库会在连接到主库时告诉主库,它是不是配置了半同步。
- 如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,结果有两种可能,要么至少一个从库节点通知它已经收到了所有这个事务的Binlog事件,要么一直等待直到超过配置的某一个时间点为止,而此时,半同步复制将自动关闭,转换为异步复制。
- 从库节点只有在接收到某一个事务的所有Binlog,将其写入并Flush到Relay Log文件之后,才会通知对应主库上面的等待线程。
- 如果在等待过程中,等待时间已经超过了配置的超时时间,没有任何一个从节点通知当前事务,那么此时主库会自动转换为异步复制,当至少一个半同步从节点赶上来时,主库便会自动转换为半同步方式的复制。
- 半同步复制必须是在主库和从库两端都开启时才行,如果在主库上没打开,或者在主库上开启了而在从库上没有开启,主库都会使用异步方式复制。
半同步复制如何实现?半同步复制实现的关键点是Master对于事务提交过程特殊处理。目前实现半同步复制主要有两种模式,AFTER_SYNC模式和AFTER_COMMIT模式。两种方式的主要区别在于是否在存储引擎提交后等待Slave的ACK。
# AFTER_COMMIT模式
Master的数据写入了binlog,slave 刷新到磁盘(relay log),同时master需要等待slave 反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。
# AFTER_SYNC模式
这种模式(AFTER_SYNC),事务是在提交之前发送给Slave,当Slave没有接收成功,并且如果发生Master宕机的场景,不会导致主从不一致,因为此时Master端还没有提交,所以主从都没有数据,这样就能够满足数据完整性和一致性了。
这种半同步方案又叫:Loss-Less半同步复制。
# 部署实施
要想使用半同步复制,必须满足以下几个条件:
- MySQL 5.5及以上版本
- 支持动态加载,变量have_dynamic_loading为YES (查看命令:show variables like "have_dynamic_loading";)
- 主从复制已经存在
半同步复制是一个功能模块,默认并没有安装,Mysql插件目录下semisync_master.so文件编译安装后默认就有。
主库my.ini添加配置如下:
plugin-load=rpl_semi_sync_master=semisync_master.so
rpl_semi_sync_master_enabled=1
2
从库my.ini添加配置如下:
plugin-load=rpl_semi_sync_slave=semisync_slave.so
rpl_semi_sync_slave_enabled=1
2
配置完成后(重启Mysql)后可以通过命令show plugins
查看已经启用的插件列表是有刚刚安装的插件。
另外,可以通过以下命令查看半同步是否在运行:
- 主库
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)
2
3
4
5
6
7
- 从库
mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.20 sec)
2
3
4
5
6
7
Mysql半同步复制并不是严格意义上的半同步复制。当半同步复制发生超时时(由rpl_semi_sync_master_timeout参数控制,单位是毫秒,默认为10000,即10s),会暂时关闭半同步复制,转而使用异步复制。当master dump线程发送完一个事务的所有事件之后,如果在rpl_semi_sync_master_timeout内,收到了从库的响应,则主从又重新恢复为半同步复制。
mysql> show variables like '%rpl%';
+-------------------------------------------+------------+
| 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 |
| rpl_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
7 rows in set (0.01 sec)
2
3
4
5
6
7
8
9
10
11
12
13
可以设rpl_semi_sync_master_wait_point变量控制主库等待从库返回ACK信号的时间点。