主从复制概念

2020-12-06

复制可以将一台MySQL数据库服务器(源)中的数据复制到一台或多台MySQL数据库服务器(副本)中。默认情况下,复制是异步的;副本无需永久连接即可从源接收更新。根据配置,您可以复制数据库中的所有数据库,选定的数据库甚至选定的表。副本将有关已配置的源的信息存储在其连接元数据存储库中,复制用户名和密码以纯文本格式存储在复制元数据存储库中。

优点

横向扩展:所有写入和更新都必须在复制源服务器上进行。但是,读取可能发生在一个或多个副本上。该模型可以提高写入性能(因为源专用于更新),同时可以在越来越多的副本中显着提高读取速度。
数据安全性:因为数据已复制到副本,并且副本可以暂停复制过程,所以可以在副本上运行备份服务而不会破坏相应的源数据
分析:可以在源上创建实时数据,而信息分析可以在副本上进行,而不会影响源的性能
数据分发:可以使用复制来创建数据的本地副本以供远程站点使用,而无需永久访问源

复制元数据库

副本服务器创建两个复制元数据存储库,即连接元数据存储库和应用程序元数据存储库。复制元数据存储库在复制服务器关闭后仍可生存。如果使用的是基于二进制日志文件位置的复制,则当副本重新启动时,它将读取两个存储库以确定先前从源读取二进制日志并处理其自身的中继日志时进行了多长时间。如果正在使用基于GTID的复制,则副本不会出于该目的使用复制元数据存储库,但会将其用于包含的其他元数据。
副本的连接元数据存储库包含复制I/O线程连接到复制源服务器并从源的二进制日志中检索事务所需的信息。此存储库中的元数据包括连接配置,复制用户帐户详细信息,连接的SSL设置,以及复制I/O线程当前正在从源二进制日志读取的文件名和位置。
副本的应用程序元数据存储库包含复制SQL线程读取和应用副本的中继日志中的事务所需的信息。此存储库中的元数据包括复制SQL线程已在中继日志中执行事务的文件名和位置,以及源二进制日志中的等效位置。它还包括用于应用事务处理的元数据,例如工作线程数。
复制元数据库默认以文件形式保存在data目录,master.info和relay-log.info;使用由master-info-file和 relay_log_info_file可以指定的替代名称和位置
指定master_info_repository=TABLE和relay_log_info_repository=TABLE可以将复制元数据存储在表中mysql.slave_master_info,mysql.slave_relay_log_info
副本的连接元数据存储库包含复制I/O线程连接到复制源服务器并从源的二进制日志中检索事务所需的信息。连接元数据存储库被写入mysql.slave_master_info表或master.info文件
副本的应用程序元数据存储库包含复制SQL线程读取和应用副本的中继日志中的事务所需的信息。应用程序元数据存储库被写入mysql.slave_relay_log_info表或relay-log.info文件中。
RESET SLAVE清除复制元数据存储库中的数据,但复制连接参数除外

复制方式

基于二进制日志复制

源将数据的更新和更改作为事件写入二进制日志,将副本配置为读取二进制日志,并在副本本地执行二进制日志中的事件
副本将接收源的全部二进制日志内容,副本可以指定执行指定的事件或全部事件。
副本记录着二进制日志的文件和做坐标,已从源读取并处理指定的二进制日志的指定位置
源上不能使用skip_networking禁用网络连接

基于GTID复制

每个事务都可以在提交到原始服务器上并由任何副本应用时进行标识和跟踪。不需要引用日志文件和指定这些文件中的位置,这大大简化了复制
GTID复制完全基于事务,很容易确定源和副本的一致性
GTID可以使用基于语句和基于行的复制,官方建议使用基于行的复制(默认配置),以获取最佳性能
源上的事务在副本上只能执行一次,
复制源服务器必须启动二进制日志

限制

不允许使用CREATE TABLE ... SELECT语句,当 binlog_format设置为STATEMENT时,一条CREATE TABLE ... SELECT语句作为具有一个GTID的一个事务记录在二进制日志中,但是如果使用ROW格式,则该语句记录为具有两个GTID的两个事务。如果源使用STATEMENT格式,而副本使用ROW格式,则副本将无法正确处理事务。
事务、存储过程、函数和触发器内部不支持CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE语句
不支持使用sql_slave_skip_counter跳过事务
如果目标服务器的二进制日志中没有GTID ,则可以将使用mysqldump进行的转储导入到启用了GTID模式的MySQL服务器中

复制格式

基于语句的复制(SBR)

源会将SQL语句写入二进制日志。将源复制到副本可以通过在副本上执行SQL语句来进行(5.7.7前默认使用)binlog_format =STATEMENT

优点

成熟的技术。
写入日志文件的数据更少。当更新或删除影响许多行时,日志文件所需的存储空间大大减少。可以更快地完成备份的备份和还原。
日志文件包含所有进行了任何更改的语句,因此它们可用于审核数据库。

缺点

对于不安全的SBR语句,并不是所有修改数据的语句(如 INSERT  DELETE UPDATE REPLACE)都可以使用基于语句的复制进行复制。当使用基于语句的复制时,任何不确定性行为都很难复制。这种数据修改语言(DML)语句的例子包括:
使用以下任何功能的语句基于语句的复制无法正确复制:LOAD_FILE();UUID();UUID_SHORT();USER();FOUND_ROWS();SYSDATE()(除非源和副本都使用该--sysdate-is-now 选项启动 );GET_LOCK();IS_FREE_LOCK();IS_USED_LOCK();MASTER_POS_WAIT();RAND();RELEASE_LOCK();SLEEP();VERSION()
但是,所有其他功能都可以使用基于语句的复制正确地复制,包括 NOW()等等。
INSERT ... SELECT与基于行的复制相比,需要更多的行级锁。
UPDATE WHERE与基于行的复制相比,要求进行表扫描的语句(因为在子句中未使用索引 )必须锁定更多的行。
对于InnoDB:一个使用AUTO_INCREMENT的INSERT语句会阻塞其他非冲突的INSERT语句。
对于复杂的语句,在更新或插入行之前,必须在副本上评估并执行该语句。对于基于行的复制,副本仅需修改受影响的行,而无需执行完整语句。
如果对副本的评估存在错误,尤其是在执行复杂的语句时,基于语句的复制可能会随着时间的流逝缓慢地增加受影响行上的错误余量

基于行的复制(RBR)

源会将事件写入 二进制日志,以指示如何更改各个表行。通过将表示对表行所做的更改的事件复制到副本,可以将源复制到副本(5.7.7后默认使用)binlog_format =ROW

优点

可以复制所有更改。这是最安全的复制形式。
使用基于语句的复制将更新mysql系统数据库中的信息的语句(如GRANT,REVOKE和触发器操作、存储例程(包括存储过程)和视图)复制到副本中。
对于像CREATE TABLE…SELECT时,从表定义生成一个CREATE语句,并使用基于语句的格式复制,而行插入使用基于行的格式复制
对于任何UPDATE,DELETE或INSERT语句,副本上所需的行锁较少
对于以下类型的语句,源上需要较少的行锁,从而实现更高的并发性:
INSERT ... SELECT
INSERT 与 AUTO_INCREMENT
UPDATE或DELETE带有WHERE不使用键或不更改大多数已检查行的子句的语句。

缺点

RBR可以生成更多必须记录的数据。要复制DML语句(例如 UPDATE或DELETE语句),基于语句的复制仅将语句写入二进制日志。相比之下,基于行的复制会将每个更改的行写入二进制日志。如果该语句更改许多行,则基于行的复制可能会向二进制日志中写入大量数据;即使对于回滚的语句也是如此。这也意味着制作和还原备份可能需要更多时间。此外,二进制日志被锁定更长的时间以写入数据,这可能会导致并发问题。使用binlog_row_image=minimal大大减少了缺点。
生成大型BLOB值的确定性udf使用基于行的复制比使用基于语句的复制花费更长的时间。这是因为记录的是BLOB列值,而不是生成数据的语句。
您无法在副本上看到从源接收和执行了哪些语句。但是,您可以使用mysqlbinlog并使用base64-output=DECODE-ROWS和--verbose选项看到更改了哪些数据。

混合复制(MBR)

还可以配置MySQL以同时使用基于语句的记录和基于行的记录,这取决于哪种记录最适合记录更改binlog_format =MIXED

处理安全和不安全的语句

使用基于语句的日志记录时,标记为不安全的语句会产生警告。安全语句正常记录。
使用基于行的日志记录时,在安全和不安全语句的处理上没有区别。
使用混合格式日志记录时,标记为不安全的语句将使用基于行的格式记录;使用基于语句的格式记录被视为安全的语句

复制同步类型

同步的原始类型是单向异步复制

一台服务器充当源,而一台或多台其他服务器充当副本

半同步复制

通过插件实现
对于完全同步复制,当源提交事务时,所有副本也必须已提交事务,然后源才返回执行事务的会话。完全同步复制意味着可以随时从源故障转移到任何副本。完全同步复制的缺点是完成事务可能会有很多延迟,半同步复制介于异步复制和完全同步复制之间。源等待直到至少一个副本接收并记录了事件而不是事件已在副本端完全执行并提交,然后提交事务。因此,半同步复制可确保如果源崩溃,则它已提交的所有事务都已传输到至少一个副本。
与异步复制相比,半同步复制提供了改进的数据完整性,因为当提交成功返回时,数据至少存在两个位置。在半同步源收到所需数量的副本的确认之前,该事务处于保留状态且未提交。
与完全同步复制相比,半同步复制更快,因为半同步复制可以平衡配置对数据完整性的要求
使用半同步复制时,如果源崩溃并且执行了对副本的故障转移,则不应将发生故障的源用作复制源服务器,而应将其丢弃。它可能具有任何副本都未确认的事务,因为在故障转移之前未提交这些事务。
仅在事件已被写入其中继日志并刷新到磁盘之后,副本才确认接收到事务事件
如果在没有任何副本确认事务的情况下发生超时,则源将恢复为异步复制。至少有一个半同步副本时,源将返回到半同步复制。
必须在源端和副本端都启用半同步复制
rpl_semi_sync_master_wait_for_slave_count配置源返回到会话之前,每个事务必须收到的副本确认的数量,默认值为1。
rpl_semi_sync_master_wait_point控制半同步复制源在向提交事务的客户端返回状态之前等待事务接收的副本确认的时间点
AFTER_SYNC(默认值):源将每个事务写入其二进制日志和副本,并将二进制日志同步到磁盘。源在同步之后等待事务接收的副本确认。在接收到确认后,源将事务提交到存储引擎,并将结果返回给客户机,然后客户机就可以继续进行。所有客户端在源上看到相同的数据。在源失败的情况下,源上提交的所有事务都已复制到副本(保存到其中继日志中)。源的意外退出和对副本的故障转移是无损的,因为副本是最新的。如上所述,在故障转移之后不应该重用源。
AFTER_COMMIT:源将每个事务写入其二进制日志和副本,同步二进制日志,并将事务提交到存储引擎。源在提交后等待事务接收的副本确认。在接收到确认之后,源将结果返回给客户端,然后客户端可以继续处理。在提交之后和副本确认之前,其他客户端可以在提交客户端之前看到提交的事务。如果出现某些错误,以至于副本不处理事务,那么在意外的源退出和故障转移到副本的情况下,这些客户机可能会看到相对于它们在源上看到的数据的丢失。

延迟复制

副本故意在源之后至少指定的时间量才复制数据
目的:
防止用户在源上犯错误。DBA可以将延迟的副本回滚到灾难发生之前的时间
测试存在滞后时系统的行为。例如,在应用程序中,延迟可能是由于副本上的繁重负载引起的。但是,可能很难生成此负载级别。延迟复制可以模拟延迟,而不必模拟负载。
无需重新加载备份即可检查数据库的外观。例如,如果延迟是一周,并且DBA需要在开发最后几天之前查看数据库的结构,则可以检查延迟的副本。
START SLAVE和STOP SLAVE会立即生效,并且忽略任何延迟。RESET SLAVE会将延迟重置为0。

多源复制

多源复制使副本能够并行接收来自多个即时源的事务。在多源复制拓扑中,副本为应从中接收事务的每个源创建一个复制通道
应用事务时,多源复制不会实现任何冲突检测或解决方案,并且如果需要,这些任务将留给应用程序
在多源复制拓扑中,不能使用复制数据目录来为副本提供来自所有源的数据
使用基于GTID配置多源副本时,如果多源中已有数据时在转储的文件中执行SET @@GLOBAL.gtid_purged,因为gtid_purged只有为空时才能设置,副本导入转储的文件时,应将转储文件中的SET @@GLOBAL.gtid_purged记录下并删除,然后在导入后将多源的gtid合并设置gtid_purged

实现目标

将多个服务器备份到单个服务器
合并表碎片
将数据从多个服务器整合到单个服务器

复制线程

复制有三个主要线程,一个在源服务器上,两个在副本服务器上
每个源/副本连接都有三个主线程。具有多个副本的源为每个当前连接的副本创建一个二进制日志转储线程,并且每个副本都有自己的复制I/O和SQL线程。
副本使用两个线程将读取和更新从源中分离出来并执行为独立的任务。因此,如果应用事务的过程很慢,则读取事务的任务不会减慢

二进制日志转储线程

源创建一个线程,以便在副本连接时将二进制日志内容发送到副本。在源的SHOW PROCESSLIST输出中将该线程标识为Binlog Dump线程。二进制日志转储线程获取源的二进制日志上的锁,以读取要发送到副本的每个事件。读取事件后,即使在将事件发送到副本之前,也会释放锁定

复制I/O线程

在副本服务器上发出一条START SLAVE语句时,副本将创建一个I/O线程,该线程连接到源并要求它发送记录在其二进制日志中的更新
复制I / O线程读取源Binlog Dump线程发送的更新,并将它们复制到组成副本的中继日志的本地文件中。该线程在SHOW SLAVE STATUS的状态显示为Slave_IO_running

复制SQL线程

副本创建一个SQL线程以读取由复制I/O线程写入的中继日志,并执行其中包含的事务

启动关闭特定进程

mysql> START SLAVE IO_THREAD;
mysql> START SLAVE SQL_THREAD;
mysql> STOP SLAVE IO_THREAD;
mysql> STOP SLAVE SQL_THREAD;

监视进程

提供的有关复制的源和副本上的信息
SHOW PROCESSLIST

中继日志

中继日志与二进制日志一样,由一组编号文件和一个索引文件组成,其中编号文件包含描述数据库更改的事件,而索引文件则包含所有已使用中继日志文件的名称。
中继日志文件与二进制日志文件具有相同的格式,可以使用mysqlbinlog读取
副本服务器在以下情况下创建一个新的中继日志文件

  • 每次复制I/O线程启动。
  • 清除日志时(例如,使用 FLUSH LOGS或 mysqladmin flush-logs)
  • 当前中继日志文件的大小太大时,确定大小:
    • 如果值 max_relay_log_size大于0,则为最大中继日志文件大小。
    • 如果max_relay_log_size值为0,则最大中继日志文件大小为max_binlog_size大小。

复制SQL线程在执行每个中继日志文件中的所有事件并且不再需要该文件后,会自动删除该文件。没有明确的机制可以删除中继日志,因为复制SQL线程会这样做。但是,FLUSH LOGS重置中继日志,这会影响复制SQL线程删除它们的时间

复制限制

基于语句复制的DELETE, UPDATE以及INSERT ... SELECT的LIMIT条款,因为受影响的行的顺序没有界定的前瞻性声明是不安全的。(只有当这些语句也包含一个ORDER BY子句时,才能使用基于语句的复制正确地复制这些语句。)遇到此类语句时:

  • 使用STATEMENT模式时,现在发出警告,指出该语句对于基于语句的复制不安全。
  • 使用STATEMENT模式时,即使DML LIMIT语句也有一个ORDER BY子句,也会对其发出警告 (并因此使之具有确定性)。
  • 使用MIXED模式时,该语句现在将使用基于行的模式自动复制

只要分区表使用相同的分区方案,并且在其他结构上具有相同的结构(除非特别允许例外),就支持在分区表之间进行复制

  • 通常不支持在具有不同分区的表之间进行复制。这是因为ALTER TABLE ... DROP PARTITION在这种情况下直接作用于分区的语句,可能在源和副本上产生不同的结果。如果表在源上而不是副本上进行分区,则在副本的源上对分区进行操作的任何语句都将在副本上失败。当对表的副本进行分区但对源未分区时,作用于分区的语句无法在源上运行而不会在源上引起错误。
  • 由于存在导致复制完全失败(由于失败的语句)和不一致(当分区级SQL语句的结果在源和副本上产生不同结果时)的危险,官方建议确保对任何表进行分区要从源复制的副本与这些表的副本版本匹配。

当在已损坏或其他损坏的表上使用该REPAIR TABLE 语句时,该语句可能会删除无法恢复的行。但是,此语句对表数据所做的任何此类修改都不会被复制,这可能导致源和副本失去同步。因此,如果源上的表被损坏并REPAIR TABLE用于修复它,则应先使用停止复制(如果它仍在运行),然后再使用REPAIR TABLE,然后再比较源和副本的表副本,并在重新启动复制之前,准备手动纠正任何差异

源端的异常关闭(例如崩溃)可能会导致源二进制日志的最终位置小于副本服务器读取的最新位置,这是由于未刷新源二进制日志文件造成的。这可能导致副本在源恢复时无法复制。在源my.cnf文件中进行设置sync_binlog=1有助于最大程度地减少此问题,因为这会导致源更频繁地刷新其二进制日志。为了在InnoDB与事务一起使用的复制设置中获得最大的持久性和一致性 ,还应该设置 innodb_flush_log_at_trx_commit=1。使用此设置,InnoDB 重做日志缓冲区在每次事务提交时被写出到日志文件,并且日志文件被刷新到磁盘。注意,使用该设置仍不能保证事务的持久性,因为操作系统或磁盘硬件可能会告诉mysqld磁盘刷新操作已经发生,即使尚未进行

使用基于语句的复制时,在源上执行的触发器也将在副本上执行。使用基于行的复制时,在源上执行的触发器不会在副本上执行。取而代之的是,源于触发器执行的源行更改将被复制并应用于副本。
如果希望触发器在源和副本上都执行,可能是因为在源和副本上具有不同的触发器,所以必须使用基于语句的复制。但是,要启用副本端触发器,不必专门使用基于语句的复制。仅对需要这种效果的那些语句切换到基于语句的复制就足够了,并在其余时间使用基于行的复制就足够了。

版本兼容性

MySQL支持从一个发行版本到下一个更高发行版本的复制。例如,您可以从运行MySQL 5.6的源复制到运行MySQL 5.7的副本,从运行MySQL 5.7的源复制到运行MySQL 8.0的副本,依此类推。但是,如果源使用语句或依赖副本上使用的MySQL版本不再支持的行为,则从较旧的源复制到较新的副本时可能会遇到困难。

{/if}