MySQL高可用实践
上QQ阅读APP看书,第一时间看更新

第2章 半同步复制

2.1 MySQL半同步复制简介

直到目前的最新版本,MySQL默认依然使用异步复制策略。所谓的异步复制,指的是主库写二进制日志、从库的I/O线程读主库的二进制日志和写本地中继日志、从库的SQL线程重放中继日志,这三步操作都是异步进行的。如此选择的主要理由是出于性能的考虑,与同步复制相比,异步复制显然更快,同时能承载更高的吞吐量。不过,异步复制的缺点同样显而易见,它不能保证主从数据实时一致,也无法控制从库的延迟时间,因此它不适于要求主从数据强一致性的场景。例如,为了分解读写压力,同一个程序写主库读从库,且要求读到的数据与读主库的相同,异步复制无法满足这种强数据一致性的需求。异步复制的另一个问题是可能会有数据丢失,例如主库宕机时,已经提交的事务可能还没有传到从库上,如果此时强行进行主从切换,那么就可能导致新主库上的数据不完整。

于是在MySQL 5.5中就自然而然地引入了半同步复制,用作异步复制的可选替代方案,它具有以下特性:

  • 从库在连接主库时表明它是否支持半同步复制。
  • 如果在主库启用了半同步复制,并且至少有一个支持半同步复制的从库,则主库上执行事务提交的线程将等待,直到至少一个半同步从库确认已收到事务的所有事件(此时从库会向主库发送ACK信息,即确认信息。ACK为Acknowledgement的缩写),或者直到发生超时。
  • 只有在将事件写入其中继日志并刷新到磁盘后,从库才会确认收到事务的事件,即向主库发送ACK。
  • 如果在没有任何从库确认事务的情况下发生超时,则主库将退化为异步复制。当至少有一个半同步从库赶上时,主库恢复半同步复制。退化与恢复过程都是自动的。
  • 必须在主库和从库上都启用半同步复制,否则使用异步复制。

当主库因等待来自从库的确认而阻塞时,它不会返回执行事务的会话。阻塞结束时,主库返回到会话,然后该会话可以继续执行其他语句。此时,事务已在主库提交,并且至少一个从库已确认其事件的接收。在继续之前,主库必须收到的确认从库的数量,可使用rpl_semi_sync_master_wait_for_slave_count系统变量进行配置,默认值为1。

不只是事务提交,事务回滚时主库也会发生同样的阻塞。MySQL同时支持多个数据库引擎,当一个事务中既包含事务表又包含非事务表时,回滚即使对事务表没有影响,二进制日志中也会记录非事务表的事件,因为对非事务表的修改无法回滚并且必须发送到从库。

在未以START TRANSACTION或SET autocommit=0开启事务时,每条语句都自动隐式提交。使用半同步复制时,主库上的这类语句就像显式事务提交一样。

为了加深对半同步复制中“半”的理解,简单将其与异步和全同步复制进行比较:

  • 异步复制:主库提交事务时,将事件写入它的二进制日志,而从库在准备就绪时请求它们。主库无需等待从库的ACK回复,直接提交事务并返回客户端。异步复制不确保所有事件都能到达从库,因而无法保证数据的强一致性。
  • 全同步复制:当主库提交事务时,所有从库也将在主库返回执行事务的会话之前提交事务。这样做的缺点是完成事务可能会有很大的延迟。
  • 半同步复制:介于异步复制和全同步复制之间。主库仅等待至少一个从库接收并记录事件。它不会等待所有从库都确认收到,并且从库只需要确认接收,而不是事件已在从库处理完成和提交。

与异步复制相比,半同步复制提供了改进的数据完整性,因为当提交成功返回时,已知数据至少存在于两个位置。半同步复制确实会对性能产生一些影响,因为需要等待从库,提交速度会变慢,延迟至少是将提交发送到从库,并等待从库确认收到的一个TCP/IP往返时间(RTT,Round-Trip Time)。这意味着半同步复制最好用于低延时的网络中。