Last Updated on

前言

Mysql作为常用的结构化数据库,作为众多系统的后台数据库,保存着各种各样的重要的数据,那么数据库的备份,安全,性能就自然被非常重视。Mysql 主从复制是mysql 高可用,高性能的基础,有了这个基础,mysql 的部署会变得简单、灵活并且具有多样性,从而可以根据不同的业务场景做出灵活的调整。

但是在部署Mysql主从复制前,需要先了解Mysql主从复制的原理与方案,熟悉其中的原理才能更好的理解Mysql主从复制,自然也就能更好读懂学会部署Mysql主从复制,并将其灵活使用。

下面我就尽量详细的记录介绍一下Mysql的主从复制原理与各种方案选择。

正文

1. Mysql主从复制原理

MySQL主从复制涉及到三个线程,一个运行在主节点(log dump thread),其余两个(I/O thread, SQL thread)运行在从节点,如下图所示:

如上图所示,当从节点连接主节点时,主节点会创建一个log dump 线程,用于发送bin-log的内容,当从节点启动时,会从节点会创建一个I/O线程用来连接主节点,请求主库中更新的bin-log。I/O线程接收到主节点binlog dump 进程发来的更新之后,保存在本地relay-log中。SQL线程负责读取relay log中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。

当主节点有多个从节点时,主节点会为每一个当前连接的从节点建一个binary log dump 进程,而每个从节点都有自己的I/O进程,SQL进程。

复制的基本过程如下:

  1. 从节点上的I/O 进程连接主节点,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
  2. 主节点接收到来自从节点的I/O请求后,通过负责复制的I/O进程根据请求信息读取指定日志指定位置之后的日志信息,返回给从节点。返回信息中除了日志所包含的信息之外,还包括本次返回的信息的bin-log file 的以及bin-log position;从节点的I/O进程接收到内容后,将接收到的日志内容更新到本机的relay log中,并将读取到的binary log文件名和位置保存到master-info 文件中,以便在下一次读取的时候能够清楚的告诉Master“我需要从某个bin-log 的哪个位置开始往后的日志内容,请发给我”
  3. Slave 的 SQL线程检测到relay-log 中新增加了内容后,会将relay-log的内容解析成在祝节点上实际执行过的操作,并在本数据库中执行。

2. Mysql主从复制模式

MySQL 主从复制有异步模式、半同步模式、全同步模式,默认的是异步模式。分别介绍如下:

异步模式:

如上图,就是主服务接受到客户端的操作请求,则正常执行然后记录到binlog日志后,就直接返回给客户端结果。在此期间不会考虑binlog日志是否完整传输到从服务器以及是否完整存放到从服务器上的relay日志中。主节点的log dump线程再去发送日志到从服务。这种模式对客户端的响应返回最快,但是一旦主服务(器)宕机,数据就可能会因为没有及时发送给从服务而导致从服务数据丢失。

半同步模式

如上图,这种模式下,当主服务写入binlog日志后,就会通知log dump线程发送日志到从服务的I/O线程,等待从服务将日志写入到relay log中,然后返回给主服务一个确认通知,主服务只要接受到一个从服务的确认通知,就会返回给客户端结果,否则需要等待直到超时时间然后切换成异步模式再返回给客户端结果。这样做的目的可以使主从数据库的数据延迟缩小,可以提高数据安全性,确保了事务提交后,binlog至少传输到了一个从节点上,虽然不能保证从节点将此事务更新到db中,但确保了binlog日志已经记录到relay log中。性能上会有一定的降低,客户端响应时间会变长。

全同步模式

此中模式就是在半同步的基础上,等待从服务的I/O线程和SQL线程都执行完毕,将数据更新到从服务器后,返回确认信息后,主服务才返回给客户端成功信息。此中模式完全保证了主从服务的数据一致性,但是相应的响应时间会变长,性能较低。

3. Mysql主从复制方式

上面讲了常见的集中主从复制模式,下面说一下主从复制的方式:

传统方式:

传统方式即是基于binlog日志的position点来指定日志从什么位置开始执行增量同步,如果这个设置的不对,就会导致主从数据不一致。

GTID方式:

基于GTID的复制是从Mysql5.6开始支持的一种新的复制方式,此方式与传统基于日志的方式存在很大的差异,在基于GTID的复制中,首先从服务器会告诉主服务器已经在从服务器执行完了哪些事务的GTID值,然后主库会有把所有没有在从库上执行的事务,发送到从库上进行执行,并且使用GTID的复制可以保证同一个事务只在指定的从库上执行一次,这样可以避免由于偏移量的问题造成数据不一致。

什么是GTID,也就是全局事务ID,其保证为每一个在主上提交的事务在复制集群中可以生成一个唯一的ID。

4. Mysql主从复制选择

但是Mysql有这么多的复制方法方式,该如何选择最优的复制方式呢,下面就来对比一下这些复制的方法模式之间的优劣,以便大家作出选择:

binlog格式选择:

binlog作为复制的基础,所有复制模式都基于binlog,binlog的格式有三种,如下:

STATEMENT:基于SQL语句的复制(statement-based replication,SBR),只记录执行的sql,不需要记录每一行数据的变化,减少bin-log日志量,节约IO,提高性能,但是由于记录的执行语句,所以为了让这些语句在slave端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,且对非确定性时间,无法保证主从复制数据的一致性,比如uuid()。从而导致主从数据一致性,造成复制链路中断。所以一般为了复制都不会使用此格式。

ROW:基于行的复制(row-based replication,RBR),日志中会记录每一行数据被修改的形式,然后在slave端再对相同的数据进行回放,仅仅只需要记录那一条记录被修改了,修改成什么样了。所以日志内容会非常清楚的记录下每一行数据修改的细节。任何情况都可以被复制,这对复制来说是最安全可靠的,但是所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。对于性能要求不高的,数据量不大的可以采用此日志记录格式。

MIXED:混合模式复制(mixed-based replication,MBR),就是上面两种格式的混合,由mysql根据语句和操作选择记录日志的方式,将上面两种方式的优点都集合了,所以推荐使用此日志格式

复制方式选择:

传统方式:基于日志点复制,MySQL最早支持的复制技术,BUG相对较少。对sql查询没有什么限制。故障处理比较容易。但是故障转移时重新获取master的日志点信息比较困难。基于日志点复制是从master的binlog的偏移量进行增量同步。如果指定错误会造成遗漏或者重复,造成主从不一致。

GTID方式:可以很方便的进行故障转移,记录master最后事务的GTID值。比如master:A,slave:B,C。当A挂了后,B执行了所有A传过来的事务。当C连接到B后,在自己的binlog找到最后一次A传过来的GTID。然后C将这个GTID发送给B,B获取到这个GTID,就开始从这个GTID的下一个GTID开始发送事务给C。这种自我寻找复制位置的模式减少事务丢失的可能性以及故障恢复的时间。且slave不会丢失master的任何修改。虽然不支持非事务引擎,故障处理比较复杂,但作为5.6新引入的功能,以取代旧的传统方式。还是推荐使用GTID方式

复制模式选择:

异步复制模式没发保证数据一致性,全同步复制则性能地下,需要过多等待,对于一些对数据一致性和延迟性要求非常高的情况,可以使用全同步模式,但对于一般情况,对数据延迟性可以接受一点的,推荐使用半同步模式进行复制

5. Mysql主从延迟问题优化

优化历史简述

Mysql的主从复制中,绕不开的就是延迟问题,因为不管采用哪种方式,除非采用全同步模式进行复制,不然就会有数据延迟问题,但是全同步的化,响应太慢,对于一些大的,复制的sql,性能低到没法用。所以在采用半同步和GTID方式的情况下,主从数据复制的延迟问题就成了最大的问题。

因为最早的主从复制模型中,只有一个SQL线程负责执行Relay log,也就是说所有在主机上的操作,在从机上是串行回放的。这就带来一个问题,因为主机是可以并行执行的,如果主上写入压力比较大,那么从上的单线程回放速度很有可能会一直跟不上主,就导致了主从数据的延迟会非常大。

为了解决这个问题,MySQL官方在5.6中引入了一个比较简单并行复制方案。若开启此功能,便会启动多个WorkThread,从机上原本的SQL线程就变成了负责判断事务能否并行执行并分发给WorkThread的角色,这种并行回放是Schema级别的并行(就是database级别),如果实例上有多个库将会因此收益,而如果实例上只有一个库,那么事务将无法并行回放,而且还会因多了分发的操作导致效率略微下降。

Mysql5.6还引入了Group Commit技术,是为了解决事务提交的时候需要fsync导致并发性不够而引入的。简单来说,就是由于事务提交时必须将Binlog写入到磁盘上而调用fsync,这是一个代价比较高的操作,事务并发提交的情况下,每个事务各自获取日志锁并进行fsync会导致事务实际上以串行的方式写入Binlog文件,这样就大大降低了事务提交的并发程度。5.6中采用的Group Commit技术将事务的提交阶段分成了Flush、Sync、Commit三个阶段,每个阶段维护一个队列,并且由该队列中第一个线程负责执行该步骤,这样实际上就达到了一次可以将一批事务的Binlog fsync到磁盘的目的,这样的一批同时提交的事务称为同一个Group的事务。Group Commit虽然是属于并行提交的技术,但是却意外解决了从机上事务并行回放的一个难题。

所以Mysql5.7 中引入了新的并行回放类型, 由参数 slave_parallel_type决定,默认值DATABASE将会采用5.6版本中的SCHEMA级别的并行回放,设置为LOGICAL_LOCK则会采用基于GroupCommit的并行回放,同一个Group内的事务将会在Slave上并行回放。5.7 中引入的基于Logical_Lock极大的提高了在主机并发压力比较大的情况下从机上的回放速度,基本上做到了主机上如何提交的,在从机上如何回放。

虽然在Mysql5.7中已经表现不错了,但还是有不尽如人意的地方,这在之后的Mysql8.0中又进一步的优化了:基于WriteSet的并行复制方案,此方案号称是彻底解决困扰MySQL运维人员多年的复制延迟问题。由于本人对Mysql8.0也没有太多研究,就不多说了,有兴趣的可以自行去了解。

配置优化

为了优化延迟问题,需要开启上面说到的并行复制和组提交技术,具体的配置如何设置就不在这里列出了,具体的会在后面我们动手搭建主从复制环境时,在配置时统一进行说明,这里只做原理介绍。

结束

Mysql的主从复制的原理和一些方案的说明和选择大致就讲这么多,可能并没有说的很详细,意只在让大家先理解熟悉Mysql的主从复制,为后面动手搭建主从复制埋下基础。

有任何问题,欢迎留言。