Mysql之复制

本文阅读 33 分钟
首页 代码,Java 正文

复制解决的基本问题是一台服务器的数据与其他服务器保持同步。Mysql内建的复制功能是构建基于Mysql的大规模、高性能应用的基础,这类应用使用所谓的水平扩展。一个主库的数据可以同步到多台从库上,从库本身也可以被配置成另外一台服务器的主库。 需要注意的是,随着复制技术的不断迭代升级,mysql复制大部分是向后兼容的,也就是说从库的版本不能低于主库的版本,反过来老版本作为新版本的备库通常是行不通的。

下面我们以Mysql5.6版本为基础来讲解mysql复制的过程及原理,同时会介绍复制功能在mysql5.7甚至8.0中改进。

二进制日志文件BINLOG的格式有三种:

  • Statement:基于SQl语句级别的binlog,每条修改数据的SQL都会保存到binlog里。
  • Row:基于行级别,记录每一行数据的变化,也就是将每行数据的变化都记录到binlog里面,记录的非常详细,但是并不记录原始SQL;在复制的时候,并不会因为存储过程或触发器导致主从数据不一致的问题,但是记录的日志较Statement格式要大得多。
  • Mixed:混合Statement和Row模式,默认情况下采用Statement模式记录,某些情况下会切换到Row模式,例如SQL中包含与时间、用户相关的函数等。

同时也对应了Mysql复制的3种技术:

  • binlog_format=Statement:基于sql语句的复制,Mysql5.1.4及其之前仅提供这种复制技术。
  • binlog_format=Row:基于行的复制。
  • binlog_format=Mixed:混合复制模式,混合了基于SQL语句的复制和基于行的复制。 对于Mysql5.7.6之前的版本,Mysql的默认设置是基于SQL语句的复制,从Mysql5.7.7开始之后的版本,Mysql的默认设置变为Row的复制。

这些方式都是通过在主库上记录二进制日志,在从库重放日志的方式来实现异步的数据复制。这就意味着,在同一时间点备库上的数据可能与主库存在不一致,并且无法保证主从之间的延迟。一些大的语句可能导致备库产生几秒、几分钟甚至几个小时的延迟。

  • 数据分布 Mysql复制通常不会对带宽造成很大的压力,但是5.1版本引入的基于行的复制会比传统的基于语句的复制模式的宽带压力更大。我们可以随意地停止和开始复制工作,并且不同的地理位置来分布数据备份。及时在不稳定的网络环境下,远程复制也可以工作。但如果为了保持很低耳朵复制延迟,最好有一个稳定的低延迟链接。
  • 负载均衡 通过Mysql的复制可以将读操作分布到多个服务器上,实现对读密集型应用的优化,并且实现很方便。
  • 备份 对于备份来说,复制是一项很有意义的技术补充,但复制既不是备份也不能够取代备份。
  • 高可用性和故障切换
  • Mysql升级测试

总的来说,复制有三个步骤:

  • 在主库上把数据更改记录到二进制日志(Binary log)中(这些记录被称为二进制日志事件)。
  • 从库将主库中的日志复制到自己的中继日志(Relay log)中。
  • 从库读取中继日志中的事件,将其重放到备库数据之上。

以上只是概述,其实每一步的实现都很复杂,如下:

  1. 在主库上记录二进制日志。在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志日志中。Mysql会按事务提交的顺序而非每句语句的执行顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务了。
  2. 从库将主库的二进制日志复制到本地的中继日志中。首先从库会启动一个工作线程,称为I/O线程,I/O线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制转储线程(binlog dump),这个二进制转储线程会读取主库上的二进制日志中的事件。它不会对事件进行轮询。如果该线程追赶上了主库,它将进入睡眠状态,直到主库发送信号量通知其有新的事件产生时才会被唤醒,从库I/O线程会将接收到的事件记录到中继日志中。
  3. 从库的SQL线程执行最后一步,该线程从中继日志中读取事件并在从库执行,从而实现从库数据的更新。当SQl线程追赶上I/O线程时,中继日志通常已经在系统缓存中,所以中继日志的开销很低。SQl线程执行的事件也可以通过配置选项来决定是否写入自己的二进制日志中。

需要注意的是,以上只是复制的原理,在不同Mysql版本中具体实现有所不同,这些不同主要是为了不断的优化复制的性能。另外,Mysql也增加了一些额外的技术来保证复制过程中的数据安全,如:半同步复制或增强半同步复制。

异步复制和多线程复制

异步复制其实就是上面所介绍的传统复制方式,而多线程复制是从Mysql5.7开始带来的全新的复制技术,当然也是在传功复制方式的基础上改进来的,解决了当master同一个schema下的数据发生了变更,从库不能并发应用的问题,同时也真正将binlog组提交的优势充分发挥出来,保障了从库并发应用relay log的能力。 由于传统的复制方式的过程上面已经介绍过,下面我们重点讲一下多线程复制对于传统复制方式的改进。 所谓多线程复制技术,主要是指从库中多个SQL线程并行重放中继日志,这样可以提高从库重放日志的性能,进而降低主从复制延迟。但是这里的并行执行是有限制的并不是对中继日志整体的进行并行执行。多线程复制依赖主库commit时刻的时间戳,从binlog中的last_committed和 sequence_number可以来判断,相同last_committed作为一组事务,可以并行执行(不同last_committed有时也是可以并行执行的,这是Mysql8.0中的改进,后面会讲到)。当然要实现多线程复制以及Mysql8.0版本中的改进都是需要进行相应的参数设置的,这些会在下面配置复制模块中详细介绍,这里不在赘述。

半同步复制和增强半同步复制

上面我们讲了复制在不同版本之间的改进,新版本的复制技术已经让性能有了很大的提升了,那为什么还要加入半同步复制增强半同步复制呢?其实Mysql引入半同步复制增强半同步复制技术并不是为了提升复制的性能,相反有可能会降低复制的性能,不过它们却能为复制的数据安全性提供更可靠的保障。

前面介绍的复制都是异步操作,主库和从库的数据之间难免会有一定的延迟,这样存在一个隐患:当在主库上写入一个事务并提交成功,而从库尚未得到主库的binlog日志时,主库由于磁盘损坏、内存故障、断电等原因意外宕机,导致主库上该事务binlog丢失,此时从库就会损失这个事务,从而造成主从不一致。

为了解决这个问题,从Mysql5.5开始,引入了半同步复制机制,暂且称此时的技术为传统的半同步复制,等该技术发展到mysql5.7后已经演变为增强半同步复制(也称无损复制)。 半同步复制机制实现过程:我们知道在异步复制时,主库执行Commit提交操作并写入binlog日志后即可成功返回客户端,无须等待binlog日志传给从库。而半同步复制时,为了保证主库上的每一个binlog事件都能够被可靠地复制到从库上,主库在每次事务成功提交时,并不及时反馈给前端应用用户,而是等待至少一个从库也接收到binlog事件成功写入中继日志后,主库才返回commit操作成功给客户端。 半同步复制保证了事务成功提交后,至少有两份日志记录,一份在主库的binlog日志上,另一份在至少一个从库的中继日志Relay log上,从而进一步保证了数据的完整性。

在传统的半同步复制中,主库写数据到binlog,且执行commit操作后,会一直等待从库的ACK,即从库写入Relay log后并将数据落盘,返回给主库消息,通知主库可以返回前端应用操作成功,这样会出现一个问题,就是实际上主库已经将该事务commit到了事务引擎层,应用已经可以看到数据发生了变化,只是在等待返回而已,如果此时主库宕机,有可能从库还没能写入Relay log,这就会发生主从数据不一致。增强半同步就是为了解决这个问题而进行的改进,即主库写数据到binlog后,就开始等待从库的应答ACK,直到至少一个从库写入Relay log后,并将数据落盘,然后返回给主库消息,通知主库可以执行commit操作,然后主库开始提交到事务引擎层,应用此时可以看到数据发生了变化。

半同步复制模式下,如果在传送binlog日志到从库时,从库宕机或者网络故障,导致binlog并没有及时传送到从库上,此时主库上事务会等待一段时间(时间由参数rpl_semi_sync_master_timeout设置的毫秒数控制),如果binlog在这段时间内都无法成功发送到从库上,则Mysql自动调整复制为异步模式,事务正常返回提交结果给客户端。

需要注意的是,半同步复制很大程度上取决于主从库之间的网络情况。通俗的说,主从之间网络越快,从库越实时。另外一点,半同步模式是通过一个插件来实现的,主从库使用不同的插件,至于如何安装这里不再详述,大家自行搜索。

由于Mysql不同版本之间使用不同的复制技术,所以在配置msql时设置的配置项也是不同的,这里我们先将传统复制记录的配置过程,然后在讲解高版本中需要注意的配置参数,这样更有助于大家理解。 传统异步复制配置过程如下

  1. 确保主从库上安装了相同版本的数据库。因为主从库的角色可能会互换,同事减少问题出现的概率,所以在可能的情况下推荐安装最新的稳定版本。
  2. 在主库上,设置一个复制使用的账户,并授予REPLICATION SLAVE权限。
  3. 修改主库的配置文件my.cnf,开启binlog,并设置server-id的值。这两个参数的修改需要重新启动数据库服务才能生效。
  4. 在主库上,设置读锁定有效,这个操作是为了确保没有数据库操作,以便获得一个一致性的快照
  5. 然后得到主库上当前的二进制日志名和偏移量值。这个操作的目的是为了在从数据库启动以后,从这个点开始进行数据的恢复。
  6. 现在主库已经停止了更新操作,需要生成主库的备份。
  7. 主库备份完毕后,可以恢复写操作,剩下的操作只需在从库上执行。
  8. 将主库的一致性备份恢复到从库上。如果是使用.tar打包的文件包,只需要解开到相应的目录即可。
  9. 修改从库的配置文件my.cnf,增加server-id参数。
  10. 在从库上,使用–skip-slave-start选项启动从库,这样不会立即启动从库上的复制进程,方便对从库的服务进一步的配置。
  11. 对从库做相应的配置,指定复制使用的用户,主库ip、端口以及开始复制的日志文件和位置。
  12. 在从库上,启动slave线程
  13. 在从库上执行show processlist命令查看复制进程内容
  14. 最后测试主从复制的正确性。

在上述过程的基础上,我们来看一下Mysql5.7引入多线程复制后的相关配置:

  • 在从库上确认一下参数的配置。
    - slave_parallel_type:默认是DATABASE,兼容以前不同schema的并行复制,LOGICAL_CLOCK是标识逻辑时间戳,即多线程复制依赖主库commit时刻的时间戳。 - slave_parallel_workers默认为0,表示禁用多线程复制;当设置该值大于1时,例如slave_parallel_workers=4,表示有8个线程执行应用。 - slave_preserve_commit_order默认是OFF,表示复制应用的过程中,不保留主库事务提交的顺序性,如果从库也会有业务端访问,需要考虑是否要设置为ON,以此来保证和主库事务提交顺序的一致性。 - relay_log_recovery:设置为ON - master_info_repository:设置为TABLE - relay_log_info_repository:设置为TABLE

在Mysql8.0中,多线程复制又进行了技术更新,引入了writeset的概念,在之前的版本中,如果主库的同一会话顺序执行多个不同相关对象的事务,这两个事务是不能并行执行的,writeset技术解决了这个问题,使得复制时性能更高。配置过程如下:

  • 参照上述过程,准备好主从复制环境,但注意要使用Mysql8.0及以上版本
  • 主库执行事务前,要确认主库的以下参数:
    - transaction_write_ser_extraction:默认为OFF,表示不启用writeset,设置为XXHASH64表示启用writeset。这个参数也是启用写集(writeset)的前提条件,定义了事务中写集的算法。 - binlog_transaction_dependency_tracking:表示从库并发执行的依赖信息源,即当主库执行事务,产生binlog后,这些事务能否在开启多线程复制的从库并发执行。此参数有三个可选值: COMMIT_ORDER:表示依赖信息由主库提交时的时间戳产生 WRITESET: 表示依赖信息由主库的write set产生,任何写入不同元组的事务能够并发执行,可以简单理解为,当主库的同一个会话更新不同记录时,从库也可以并行执行。这里有个特殊情况,如果被更新的表不包括或者包含了外键,即便设置了WRETESET,也等同于参数值COMMIT_ORDER。 WRiTESET_SESSION:与WRITESET类似,只不过此时又多了一个约束,即使多个操作数据并无冲突,但如果这些操作发生在同一会话中,也等同于参数值COMMIT_ORDER。

一主库多备库

一主多备的配置和一主一备的配置差不多,因为备库之间根本没有交互。在有少量写和大量读时,这种配置是非常有用的。可以把读分摊到多个备库上,直到备库给主库造成了太大的负担,或者主备之间的带宽成为了瓶颈为止。

主动-主动模式下的主-主复制

主-主复制(也叫双主复制或双向复制)包含两台服务器,每一个都被配置成为对方的主库和备库,换句话说,他们是一对主库。 这种配合最大的问题是如何解决冲突,两个可写 的互主服务器导致的问题非常多。所以要尽量避免使用这种方式

主动-被动模式下的主-主复制

这是前面描述的主主结构的变体,它能够避免前面描述的主主模式的问题。这也是构件容错性和高可用性系统的非常强大的方式,主要区别在于其中的一台服务器是只读的被动服务器。主动-被动模式下的主-主结构能够帮助回避许多Mysql的问题和限制。 设置主动-被动模式下的主-主拓扑结构在某种意义上类似于创建一个热备份,但是可以使用这个“备份”来提高性能,例如,用它来执行读操作、备份、“离线”维护以及升级等。真正的热备份做不了这些事情。然而,这种模式不会获得比单台服务器更好的写性能

拥有备库的主-主结构

这种拓扑结构只是为每个主库增加一个备库。这种配置的有点是增加了冗余,对不同地理位置的复制拓扑,能够消除站点单点失效的问题。还可以再某个主库出现问题时,将它的备库变成主库;当然也可以让它的备库指向其他的主库。

环形复制

每个服务器都是在它之前的服务器的备库,是在它之后的服务器额主库。这种结构也称为环形复制。这种结构没有双主结构的一些优点,例如对称配置和简单的故障转移,并且完全依赖于环上的每一个可用节点,这大大增加了整个系统失效的几率。总得来说,环形结构非常脆弱,应该尽量避免

主库、分发主库以及备库

这种模式其实是一主多备模式的变体。前面在讲一主多备模式时说过如果备份服务器的数量过多时,会给主库带来很大的压力:一方面,每个备库会在主库上创建一个线程,并执行binlog dump命令。该命令会读取二进制日志文件中的数据并将其发送给备库。每个备库都会重复这样的工作,它们不会共享binlog dump的资源。当备库很多时,如果有大的事件时,主库可能由于备库同时请求同样的事件而耗尽内存并崩溃。另一方面,如果备库请求的数据不在文件系统的缓存中,可能会导致大量的磁盘检索,这同样会影响主库的性能并增加锁的竞争。

因此,如果需要多个备库时,一个较好的办法是从主库移除负载并使用分发主库。分发主库实际也是一个备库,它的唯一目的就是提取和提供主库的二进制日志。多个备库连接到分发主库,这使原来的主库摆脱了负担。为了避免在分发主库上做实际的查询,可以将它的表修改为blackHole存储引擎。 很难说当备库数据达到什么时需要一个分发主库。按照通用准则,如果主库接近满负载,不应该为其建立10个以上的备库。当然,也不一定只使用一个分发主库。 还可以通过分发主库实现其他的目的,例如,对二进制日志事件执行过滤和重写规则。 不过使用分发主库也是有缺点的:第一,如果将分发主库的表设置成blackhole,blackhole本身存在一定的bug,例如在某些情况下会忘记将自增ID写入到二进制日志中。第二,如何确保分发服务器上的每个表都是blackhole存储引擎。第三,使用分发主库另一个主要缺点是,无法使用一个备库来替代主库。因为由于分发主库的存在,导致各个备库与原始主库的二进制日志坐标已经不相同。

树或金字塔形

如果再将主库复制到大量的备库中。不管是吧数据分发到不同的地方,还是提供更高的读性能,使用金字塔结构都能够更好的管理。这种设计的好处是可以减轻主库的负担。它的缺点是中间层出现的任何错误都会影响到到个服务器。

监控复制

复制增加了Mysql监控的复杂性,尽管复制发生在主库和备库上,但大多数工作是在备库上完成的,这也正是最常出问题的地方。是否所有的备库都在工作?最慢的备库延迟是多大?Mysql本身提供了很多可以解决上述问题的信息,但是这种监控最好能实现自动化监控。 常见的命令:

  • show master status 查看当前主库的二进制日志位置和配置
  • show binlog events 查看复制事件
  • show slave status 此命令输出的seconds_behind_master列理论上显示了备库的延迟时间。但由于各种各样的原因,并不总是准确的。最好的解决办法是使用heartbeat record,这是一个在主库上会每秒更新一次的时间戳。为了计算延迟,可以直接用备库当前的时间戳减去心跳记录的值。包含在Percona Toolkit里的pt-heartbeat脚本是“复制心跳”最流行的一种实现。

确定主备是否一致

理想情况下,备库和主库的数据应该是完全一样的。但事实上备库可能发生错误并导致数据不一致。即便没有明显的错误,备库也可能因为mysql本身的特性导致数据不一致,例如:Mysql的bug,网络中断、服务器崩溃等。 因此,主备一致应该是一种规范,而不是例外,也就是说,检查主备一致应该是一个日常工作,特别是当使用备库来做备份时尤为重要。 Percona Toolkit里的pt-table-checksum能够实现我们的目标。其主要特性是用于确认备库和主库的数据是否一致。工作方式是通过在主库上执行insert…select查询。通常情况下可以在主库上运行改工具,参数如下: $ pt-table-checksum – replicate=test.checksum,该命令将检查所有的表,并将结果插入到test.checksum表中。当查询在备库执行完后,就可以简单的比较主备之间的不同了。pt-table-checksum能够发现服务器所有的备库,在每台备库上的运行查询,并自动地输出结果。目前,pt-table-checksum是唯一能够有效地比较主备一致性的工具。

从主库重新同步备库

在日常开发中,可能不止一次的需要处理未被同步的备库。传统的修复不一致的做法是:先关闭备库并从生产环境移除,然后重新从主库复制一份数据回复备库,这种方法的缺点是不太方便,特别是数据量很大时。 如果能够找到并修复不一致的数据,要比从其他服务器上重新克隆数据要有效得多。如果发现发现的不一致并不严重,就可以保持备库在线,并重新同步受影响的数据。最简单的办法是使用mysqldump转储受影响的数据并重新导入。在整个过程中,如果数据没有发发生变化,这种方法会很好。你可以在主库上简单地锁住表然后进行转储,再等待备库赶上主库,然后将数据导入到备库中。 这种方法在许多场景下是可行的,但在一个繁忙的服务器上有可能行不通。另外一个缺点是在备库上通过非复制的方式改变数据,这可不安全,可能会导致意料之外的事情。 pt-table-sync是Percona Toolkist中的工具,它可以高效的找出并安全的解决表之间的不同。它同样通过复制工作,在主库上执行查询,在备库上重新同步,这样就没有竞争条件。

为备库指定新的主库

这种情况也是比较常遇到的,其实为备库指定新的主库是比较简单的,只需在备库简单地使用CHANGE MASTER TO命令,并指定合适的值就行。这个过程中最难的是获取新主库上合适的二进制日志位置,这样备库才可以从和老主库相同的逻辑位置开始复制。 如果备库和新主库的位置不同,则需要找到该备库最后一条执行的事件在新主库的二进制日志中的相应的位置,然后再执行CHANGE MASTER TO。可以通过mysqlbinlog工具来找到备库执行的最后一条查询,然后再主库上找到同样的查询,进行简单的计算即可得到。 需要注意的是,如果是将一个备库提升为主库,千万不要将它的服务器ID修改为原主库的服务器ID,否则将不能使用日志服务器从一个旧主库来重放日志事件。这也是确保服务器ID最好保持不变的原因之一。

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/qq_38571892/article/details/119726834
-- 展开阅读全文 --
Web安全—逻辑越权漏洞(BAC)
« 上一篇 03-13
Redis底层数据结构--简单动态字符串
下一篇 » 04-10

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复