• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

MySQL主从复制原理

MySQL 彭东稳 8年前 (2016-04-09) 28023次浏览 已收录 0个评论

一、MySQL性能扩展

在企业中,最开始业务规模小的时候,基本一台数据库就可以承载压力。但是如果将应用所有的数据简单地放到单个MySQL服务器实例上,则无法很好地扩展,迟早会碰到性能瓶颈。对于许多类型的应用,传统的解决方法是购买更多强悍的机器,也就是常说的“垂直扩展”或者“向上扩展”。另外一个与之相反的方法是将任务分配到多台计算机上,这通常被称为“水平扩展”或者“向外扩展”。

  • 对于垂直扩展

意味着购买更多性能强悍的硬件,对很多应用来说这是唯一需要做的事情。但是在现代硬件上MySQL能扩展的合理值为256RAM,32核CPU以及一个PCIE flash驱动器,并且需要使用尽可能最新的MySQL版本。如果在此基础上继续提升硬件的配置,MySQL的性能虽然还能提升,但性价比就会降低。

  • 对于向外扩展

一般策略划分为三个部分:复制、拆分、以及数据分片。

复制:其中最简单的也最常见的就是通过复制将数据分发到多个服务器上,然后将从库用于查询,主库用于写入。这种技术对于以读为主的应用很有效,因为它的从服务器可以向外扩展。但对于写数据的压力没有任何帮助且还会增加写数据库的压力。

拆分:数据库拆分一般可以按照功能拆分或者说是业务拆分,也就是把不同的功能或者是不同的业务尽可能地拆分开,然后把各个功能或业务需要的数据库独立运行,也是一种很好的方式。可想而已肯定可以减小写入和读取数据的压力。

分片:数据分片是对于那些功能或业务不可拆分且数据库压力大的解决方案,也是目前用于扩展大型MySQL应用的方案。将一个大的数据库进行数据分片是最通用的方法,它把数据分割成一小片,或者说一块,然后存储到不同的节点中,当然数据分片操作在MySQL中是非常有难度的,MySQL天生就不太适应数据分片,但企业中也有这么干的。对于数据分片目前最好的解决方案就是NoSQL了,如MongoDB自动解决数据分片,当然也需要根据业务而选择如何分片。

二、MySQL复制功能介绍

MySQL内建的复制功能是构建大型,高性能应用程序的基础。这类应用使用所谓的“水平扩展”的架构。我们可以通过为服务器配置一个或多个备库的方式来进行数据同步,将MySQL的数据分布到多个系统上去。复制过程中一台主库(master)服务器充可以同步数据到多台从库服务器上去。从库服务器也可以配置成另外一台服务器的主库。主库和从库之间可以有多种不同的方式组合。

MySQL复制基于主服务器在二进制(binlog)日志中跟踪所有对数据库的更改(更新、删除等等)。每个从服务器从主服务器接收主服务器已经记录到其二进制日志的保存的更新,以便从服务器可以对其数据拷贝执行相同的更新,在从库重放日志的方式来实现异步的数据复制。这意味着,在同一时间点从库上的数据可能与主库存在不一致性。

MySQL的复制机制默认是异步的,什么意思呢?也就是说当客户端往主库中插入数据后,只要主库接收数据后持久化到磁盘上,保证了数据的安全性后就返回给客户端确认相应。而从库数据有没有复制,数据复制有没有成功,客户端是不关心的。比如说你的应用程序写入数据是到主库的,而查询数据是从从库查询的,那么就可能会出现查询不到数据的结果。因为从库不一定会那么快从主库把数据读取过来,或者复制数据失败,这就是异步带来的不一致性。而同步就是客户端往主库插入数据,直到从库把数据安全复制过来之后才会返回结果给客户端。可想而知,异步带来的是性能的提升,而同步会降低数据的写入效率。

复制通常不会增加主库的开销,主要是启用二进制日志带来的开销,除此之外,每个从库也会对主库增加一些负载,如网络I/O开销等。如果是从一个高吞吐量的主库上复制到多个从库,唤醒多个复制线程发送时间的开销会累加。

三、MySQL支持的复制格式

通过配置BINLOG_FORMAT参数的值,可以选择binlog的格式。参数BINLOG_FORMAT有3个可选的值:STATEMENT、ROW和MIXED,分别代表3种不同的binlog格式。

  • 基于语句的复制(STATEMENT

顾名思义,STATEMENT格式的binlog记录的是数据库上执行的原生SQL语句。这种方式有好处也有坏处。

好处就是相当简单,简单地记录和执行这些语句,能够让主备保持同步,在主服务器上执行的SQL语句,在从服务器上执行同样的语句。另一个好处是二进制日志里的时间更加紧凑,所以相对而言,基于语句的复制模式不会使用太多带宽,同时也节约磁盘空间。并且通过mysqlbinlog工具容易读懂其中的内容。

坏处就是同一条SQL在主库和从库上执行的时间可能稍微或很大不相同,因此在传输的二进制日志中,除了查询语句,还包括了一些元数据信息,如当前的时间戳。即便如此,还存在着一些无法被正确复制的SQL。例如,使用INSERT INTO TB1 VALUE(CUURENT_DATE())这一条使用函数的语句插入的数据复制到当前从服务器上来就会发生变化。存储过程和触发器在使用基于语句的复制模式时也可能存在问题。另外一个问题就是基于语句的复制必须是串行化的。这要求大量特殊的代码,配置,例如InnoDBnext-key锁等。并不是所有的存储引擎都支持基于语句的复制。

  • 基于行的复制(ROW

MySQL5.1开始支持基于行的复制,也就是基于数据的复制,基于行的更改。这种方式会将实际数据记录在二进制日志中,它有其自身的一些优点和缺点,最大的好处是可以正确地复制每一行数据。一些语句可以被更加有效地复制,另外就是几乎没有基于行的复制模式无法处理的场景,对于所有的SQL构造、触发器、存储过程等都能正确执行。主要的缺点就是二进制日志可能会很大,而且不直观,所以,你不能使用mysqlbinlog来查看二进制日志。也无法通过看二进制日志判断当前执行到那一条SQL语句了。

现在对于ROW格式的二进制日志基本是标配了,主要是因为它的优势远远大于缺点。并且由于ROW格式记录行数据,所以可以基于这种模式做一些DBA工具,比如数据恢复,不同数据库之间数据同步等。

  • 混合类型的复制(MIXED

MIXED也是MySQL默认使用的二进制日志记录方式,但MIXED格式默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。比如用到UUID()USER()CURRENT_USER()ROW_COUNT()等无法确定的函数。

四、MySQL主从复制工作原理

1.  Master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events)

2.  SlaveMaster的日志拷贝到自己的中继日志(relay log)中。

3.  Slave重新执行中继日志中的事件并放到自己的数据库中。

总的来说,复制就分为这三布,但是实际上每一步都很复杂,如图:

MySQL主从复制原理

该过程的第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二进制日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,Master通知存储引擎提交事务。

MySQL使用3个线程来执行复制功能,其中1个在主服务器上(binlog dump线程),另两个在从服务器上(I/O线程及SQL线程)。

I/O线程

上图所示,IO线程位于从实例,其作用就是作为一个客户端,建立到master实例上的TCP链接,并且发送认证信息,使用binlog同步协议信息,然后实时的去同步阻塞的读取master发送的binlog,并且设置有超时时间,为slave_net_timeout,如果在超过设置时间内没有收到master发送的日志信息,则认为主从之间的网络异常,或者master异常,进行网络重试。其生命周期开始于start slave或者start slave io_thread,结束于stop slave或者stop slave io_thread。

Dump线程

如上图所示,Dump线程位于master实例,此线程与普通线程类似,只不过接收到的是客户端发送的binlog dump命令,所以,会发送binlog给客户端。其生命周期开始于slave的start slave或者start slave io_thread,结束于stop slave或者stop slave io_thread。

SQL线程

SQL回放线程位于slave实例,主要作用就是读取relay log,并且进行回放操作。当从库I/O线程从主库获取到事件后便会写入到中继日志(relay log),然后SQL线程读取中继日志并执行日志中包含的事件。其生命周期开始于start slave或者start slave sql_thread,结束于stop slave或者stop slave sql_thread。

当从服务器发出START SLAVE时,从服务器创建一个I/O线程,以连接主服务器并让它发送记录在其二进制日志中的语句。主服务器创建一个线程将二进制日志中的内容发送到从服务器,该线程可以识别为主服务器上SHOW PROCESSLIST的输出中的binlog dump线程。

Tips:复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作,不然就可能会出现数据乱掉了。这也是导致MySQL主从复制延迟的一个重要因素,但从MySQL 5.7开始也支持从库的并行复制了,细节自行查看。

binlog Events

我们知道binlog日志用于记录所有对MySQL的操作的变更,而这每一个变更都会对应的事件,也就是Event。index文件记录了所有的binlog位置,每个binlog会有header event,rotate三个event,binlog的结构如下。

MySQL主从复制原理

常见的Event如下:

  • Format_desc:一个全新的binlog日志文件event信息。
  • Rotate :日志分割时结束event。
  • Table_map:表,列等元数据的event。
  • Query:查询,就是DDL这类的Event,如果binlog格式为STATEMENT格式,增删改都属于Qeury event。
  • Write_rows:Binlog为ROW格式时的插入event。
  • Update_rows:Binlog为ROW格式时的更新event。
  • Delete_rows:Binlog为ROW格式时的删除event。

这里不是主要讲解二进制日志,所以关于二进制更多详看:MySQL二进制日志(binlog)详细解析

五、MySQL复制的作用

1. 数据分布。

2. 主从分摊负载。

3. 高可用性和故障切换。

4. 数据备份。

5. 利用从服务器做查询。

六、MySQL复制协议

在谈这个特性之前,我们先来看看MySQL的复制架构衍生史。

在2000年,MySQL 3.23.15版本引入了Replication。Replication作为一种准实时同步方式,得到广泛应用。这个时候的Replicaton的实现涉及到两个线程,一个在Master,一个在Slave。Slave的I/O和SQL功能是作为一个线程,从Master获取到event后直接apply,没有relay log。这种方式使得读取event的速度会被Slave replay速度拖慢,当主备存在较大延迟时候,会导致大量binary log没有备份到Slave端。

在2002年,MySQL 4.0.2版本将Slave端event读取和执行独立成两个线程(IO线程和SQL线程),同时引入了relay log。IO线程读取event后写入relay log,SQL线程从relay log中读取event然后执行。这样即使SQL线程执行慢,Master的binary log也会尽可能的同步到Slave。当Master宕机,切换到Slave,不会出现大量数据丢失。

在2010年MySQL 5.5版本之前,一直采用的是这种异步复制的方式。主库的事务执行不会管备库的同步进度,如果备库落后,主库不幸crash,那么就会导致数据丢失。于是在MySQL在5.5中就顺其自然地引入了半同步复制,主库在应答客户端提交的事务前需要保证至少一个从库接收并写到relay log中。那么半同步复制是否可以做到不丢失数据呢?下面分析。

在2016年,MySQL在5.7.17中引入了一个全新的技术,称之为InnoDB Group Replication。目前官方MySQL 5.7.17基于Group replication的全同步技术已经问世,全同步技术带来了更多的数据一致性保障。相信是未来同步技术一个重要方向,值得期待。MySQL 5.7 Group Replication

根据上面提到的这几种复制协议,分别对应MySQL几种复制类型,分别是异步、半同步、全同步。

MySQL主从复制原理

  • 对于异步复制(asynchronous)

主库将事务Binlog事件写入到Binlog文件中,此时主库只会通知一下Dump线程发送这些新的Binlog,然后主库就会继续处理提交操作,而此时不会保证这些Binlog传到任何一个从库节点上。

  • 对于全同步复制(synchronous)

当主库提交事务之后,所有的从库节点必须收到,APPLY并且提交这些事务,然后主库线程才能继续做后续操作,保证了事务的一致性。这里面有一个很明显的缺点就是,主库完成一个事务的时间被拉长,性能降低。MySQL 5.7 Group Replication已经支持同步复制了,另外MySQL NDB CLUSTER也是支持同步。

  • 对于半同步复制(semisynchronous )

MySQL原本不支持半同步,后来基于Google为MySQL开发的半同步复制的插件,所以MySQL也就开始支持半同步了。这是自MySQL 5.1引入行复制后最大的改进。同步复制工作的机制处于同步和异步之间,主库只需要等待至少一个从库节点收到并且Flush Binlog到Relay Log文件即可,主库不需要等待所有从库给主库反馈。同时,这里只是一个收到的反馈,而不是已经完全执行并且提交的反馈,这样就节省了很多时间。如果在一定时间内从服务器没有响应,则会自动降级为异步复制。半同步主要是保证数据完整性防止数据丢失。

七、MySQL复制拓扑

MySQL主从复制能够有效工作的一些基本原则:

  • master必须开启binlog,这是主从复制能够工作的基本要求,slave需要同步master的binlog进行回放来同步数据。
  • 每个slave都需要设置server_id,且一个集群中所有的server_id不能够被重复。
  • 每个slave只能有一个master,但每个master可以有多个slave(MySQL 5.7开始出现多源复制,就是允许slave有多个master)。
  • 如果你设置log_slave_updates参数,某个slave可以是其它slavemaster,从而扩散master的更新,这种复制方式被称为联级复制。

对于MySQL主从复制可以有很多复杂的拓扑结构,但即使是最简单的也可能会非常灵活,一种拓扑可以有多种用途。关于使用复制的不同方式都可以很轻易地写上一本书。

不考虑复杂情况,最常用的大概有以下几种方式:

  • 一主一从模型

MySQL主从复制原理

  • 一主多从模型

MySQL主从复制原理

  • 一主多从模型(级联复制)

MySQL主从复制原理

  • 多主一从模型(多源复制)

MySQL主从复制原理

  • 双主模型

MySQL主从复制原理

八、MySQL复制过滤

复制过滤可以让你只复制服务器中的一部分数据,有两种复制过滤:一是在master上过滤二进制日志中的事件,二是在slave上过滤中继日志中的事件。

MySQL主从复制原理

复制过滤参考:MySQL主从复制过滤参数介绍

九、MySQL复制的缺点

MySQL的复制(replication)功能让人且爱且恨。MySQL复制配置简单,深受开发人员的喜欢,基于复制的读写分离方案也非常流行。而MySQL数据库高可用大多也是基于复制技术,但是MySQL复制本身依然存在部分缺陷,最为主要的问题如下:

  • 数据丢失问题(consistency)
  • 数据同步延迟问题(delay)
  • 扩展性问题(scalability)

从MySQL 5.7的lossless semi-sync replication已经解决了主从数据丢失的问题,MySQL 5.7的multi-thread slave也很大程度地解决了数据同步延迟的问题,MySQL 5.7的Group replication也很大程度地解决了扩展性问题。另外,MySQL 5.7.22 backlog了MySQL 8.0中的基于WriteSet的并行复制,可以说完全解决了主从数据延迟的问题。可以看出,MySQL正在朝着一个非常好的方向发展,未来一定会更好。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (7)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!