主从复制问题处理

2020-12-06

跳过失败事务

计算失败的事务需要跳过的事件数

停止副本
STOP SLAVE
查看正在使用的中继日志Relay_Log_File,中继日志的执行位置Relay_Log_Pos
show slave status\G
查看中继日志中的执行事件,查找出错位置的Pos,计算事件数
SHOW RELAYLOG EVENTS in 'Relay_Log_File';
跳过事件数
SET GLOBAL sql_slave_skip_counter = N
START SLAVE

使用change master to

查看正在使用的中继日志Relay_Log_File,中继日志的执行位置Relay_Log_Pos
show slave status\G
查看中继日志中的执行事件,查找出错位置的Pos
SHOW RELAYLOG EVENTS in 'Relay_Log_File';
指定新的二进制日志文件和二进制日志的位置
CHANGE MASTER TO MASTER_LOG_FILE='source_log_name', MASTER_LOG_POS=source_log_pos;

使用GTID跳过失败事务

停止副本
stop slave;
查看失败事务的GTID
select * from performance_schema.replication_applier_status_by_worker;
在副本查询GTID代表的事务(如果需要知道事务)
SHOW RELAYLOG EVENTS
在源上查询GTID代表事务(如果需要知道事务)
SHOW BINLOG EVENTS
在副本上针对失败的GTID注入空事务
SET GTID_NEXT='aaa-bbb-ccc-ddd:N';
BEGIN;
COMMIT;
SET GTID_NEXT='AUTOMATIC';
启动副本,副本将使用自动跳过来忽略失败的事务,因为它会看到该GTID已应用了事务。
start salve;
如果副本上正在使用二进制日志记录,并且该副本将来可能成为源或主副本,会将空事务进入复制流。如果需要避免这种可能性,请考虑刷新和清除副本的二进制日志,空事务的GTID会保留,但是通过清除二进制日志文件会删除事务本身。
FLUSH LOGS;
PURGE BINARY LOGS TO 'binlog.000146';

备份数据

使源只读进行备份

mysql> FLUSH TABLES WITH READ LOCK;
mysql> SET GLOBAL read_only = ON;
当源处于只读状态时:
客户端发送到源的更新请求被阻止,因为服务器处于只读模式。
客户端发送到源的查询结果请求成功。
在源上进行备份是安全的。
在副本上进行备份是不安全的。该服务器仍在运行,可能正在处理来自客户端的二进制日志或更新请求

在完成备份操作后,将源恢复到正常操作状态
mysql> SET GLOBAL read_only = OFF;
mysql> UNLOCK TABLES;
此策略适用于在复制设置中备份源服务器,但也可以用于非复制设置中的单个服务器

使备份只读进行备份

mysql> FLUSH TABLES WITH READ LOCK;
mysql> SET GLOBAL read_only = ON;
当R1处于只读状态时:
源继续运行,因此在源上进行备份是不安全的。
副本已停止,因此在副本上进行备份是安全的。

在副本上的备份操作完成后,将副本恢复到正常操作状态
mysql> SET GLOBAL read_only = OFF;
mysql> UNLOCK TABLES;

处理副本意外停止

InnoDB使用名为mysql.slave_relay_log_info表作为应用的元数据存储库。作为一个表,对应用程序元数据存储库的更新与事务一起提交,这意味着即使在服务器意外中断的情况下,记录在该存储库中的副本的进度信息也始终与已应用于数据库的信息一致
对于基于GTID的复制,恢复过程需要副本已接收或提交的事务的GTID。可以使用GTID自动定位从源中检索丢失的事务,该自动定位功能自动将源的事务与副本的事务进行比较,并标识丢失的事务。使用基于GTID的复制可以最容易地配置复制并处理意外停止
对于基于文件位置的复制,恢复过程需要一个准确的复制SQL线程(应用程序)位置,以显示在副本上应用的最后一个事务。基于该位置,复制I / O线程(接收器)从源二进制日志中检索从该点开始应在副本上应用的所有事务。

主从服务器存储引擎、列使用不同设置

修改副本表存储引擎

对于复制过程而言,源上的源表和副本上的复制表是否使用不同的引擎类型无关紧要。实际上default_storage_engine和 storage_engine系统变量不会被复制

停止副本运行复制更新:
mysql> STOP SLAVE;

对要更改的每个表执行要修改的存储引擎。 
ALTER TABLE ... ENGINE='engine_type'

再次启动复制过程:
mysql> START SLAVE;

如果要创建的表不存在特定的引擎,则MySQL通常使用默认的引擎类型
如果要在源和副本上保留引擎差异,则在创建新表时应小心使用源上的default_storage_engine变量。例如使用指定存储引擎代替默认存储引擎:
mysql> CREATE TABLE tablea (columna int) Engine=MyISAM;

复制后,该default_storage_engine变量将被忽略,并且该CREATE TABLE语句使用副本的默认引擎在副本上执行。
mysql> SET default_storage_engine=MyISAM;
mysql> CREATE TABLE tablea (columna int);

复制的源表和目标表不必相同

源上的表可以具有比表的副本更多或更少的列。此外,在一定条件下,源和副本上的相应表列可以使用不同的数据类型
在源或副本上具有更多列的复制(将表从源复制到副本,以使表的源副本和副本副本具有不同的列数,但要满足以下条件):
表的两个版本共有的列必须在源和副本上以相同顺序定义。(即使两个表具有相同的列数,也是如此。)
必须在任何其他列之前定义表的两个版本共有的列。

复制具有不同数据类型的列

理想情况下,同一表的源副本和副本副本上的对应列应具有相同的数据类型。但是,只要满足某些条件,这并不总是严格执行的:
通常可以从给定数据类型的列复制到相同类型和相同大小或宽度(如果适用)或更大的另一列。例如,您可以从CHAR(10)列复制到另一列CHAR(10),或从CHAR(10)列复制到CHAR(25)列而没有任何问题。在某些情况下,还可以从一种数据类型的列(在源上)复制到另一种数据类型的列(在副本上);当列的源版本的数据类型在副本上提升为相同大小或更大的类型时,这称为属性提升。
属性升级可以与基于语句的复制和基于行的复制一起使用,并且不依赖于源或副本使用的存储引擎。但是,日志记录格式的选择确实会影响允许的类型转换。
无论您是使用基于语句的复制还是基于行的复制,如果您希望使用属性提升,表的副本副本所包含的列都不能超过源副本的列

属性提升

基于语句的复制

使用基于语句的复制时,遵循的一条简单经验法则是:“如果在源上运行的语句也将在副本上成功执行,那么它也应成功复制”。换句话说,如果该语句使用的值与副本上给定列的类型兼容,则可以复制该语句。

基于行的复制

属性提升和降级,基于行的复制支持较小数据类型和较大类型之间的属性提升和降级。也可以指定是否允许降级的列值进行有损(截断)或无损转换

有损和无损转换

如果目标类型不能代表要插入的值,则必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现 “拟合”,那么我们进行所谓的有损转换。不需要截断或类似修改即可将源列值适合目标列的转换是一种 无损转换。

slave_type_conversions控制副本上使用的类型转换模式

ALL_LOSSY: 在这种模式下,允许进行意味着信息丢失的类型转换。 这并不意味着允许进行无损转换,仅表示仅要求有损转换或完全不进行转换的情况;例如,仅启用 此模式将允许将 INT列转换为 TINYINT(有损转换),但不允许将TINYINT列转换为 INT列(无损)。在这种情况下尝试进行后一种转换将导致复制停止,并在副本上出现错误。 
ALL_NON_LOSSY: 该模式允许不需要截断或其他特殊处理源值的转换。也就是说,它允许目标类型比源类型具有更大范围的转换。尝试(如有损的转换INT到 TINYINT,或CHAR(25)到VARCHAR(20))将导致复制停止与并产生一个错误。 
ALL_LOSSY,ALL_NON_LOSSY: 设置此模式后,将允许所有受支持的类型转换,无论它们是否是有损转换。 
ALL_SIGNED: 将提升的整数类型视为带符号的值(默认行为)。 
ALL_UNSIGNED: 将提升的整数类型视为无符号值
ALL_SIGNED,ALL_UNSIGNED: 如果可能,将提升的整数类型视为带符号,否则视为无符号。 
[空]:(默认)如果slave_type_conversions未设置,则不允许属性提升或降级。这意味着源表和目标表中的所有列都必须具有相同的类型。

支持的转换

不支持在使​​用不同字符集的列之间进行复制。
TINYINT, SMALLINT, MEDIUMINT, INT,和 BIGINT之间的任意整数类型的。这包括这些类型的带符号和无符号版本之间的转换。通过将源值截断为目标列允许的最大值(或最小值)来进行有损转换。为了确保从无符号类型到带符号类型的无损转换,目标列必须足够大以容纳源列中的值范围。
DECIMAL, FLOAT, DOUBLE,和 NUMERIC之间的任何小数类型。FLOAT转DOUBLE是无损转换;DOUBLE转FLOAT只能有损地处理
CHAR,VARCHAR以及TEXT之间的任何字符串类型,包括不同的宽度之间的转换。CHAR,VARCHAR或TEXT以一个CHAR,VARCHAR或TEXT列中的相同大小或更大是从没有损耗的。通过仅在副本上插入字符串的第一个字符N(即目标列的宽度)来处理有损转换。
BINARY, VARBINARY以及 BLOB之间的任何二进制数据类型,包括不同的宽度之间的转换。BINARY, VARBINARY或BLOB 以一个BINARY, VARBINARY或BLOB 列中的相同大小或更大是从未有损耗的。有损转换是通过仅在副本上插入字符串的第一个 字节N(即目标列的宽度)来处理的。

 

提高复制性能

创建一个更深的复制结构,该复制结构使源仅可以复制到一个副本,并使其余副本可以根据其各自的复制要求连接到该主副本

配置

源1是将所有更改和更新都写入数据库的主要源。应在此计算机上启用二进制日志记录。
源2是源1的副本,它为复制结构中的其余副本提供复制功能。源2是唯一允许连接到源1的计算机。源2还启用了二进制日志记录,并且启用了 log_slave_updates系统变量,因此来自源1的复制指令也被写入源2的二进制日志,以便可以将它们复制到源2。
副本1,副本2和副本3充当源2的副本,并复制来自源2的信息,该信息实际上由源1组成。

如果副本在跟上源上的复制过程时遇到问题,有许多可用的选项

如果可能,请将中继日志和数据文件放在不同的物理驱动器上。为此,设置relay_log系统变量以指定中继日志的位置。
如果副本比源慢得多,则可能需要划分将不同数据库复制到不同副本的责任
如果您的源使用事务,并且您不关心副本上的事务支持,请在副本上使用MyISAM或其他非事务引擎
如果您的副本不充当源,并且有适当的解决方案来确保在发生故障时可以调出源,则可以在副本上禁用log_slave_updates。这样可以防止副本也将已执行的事件记录到自己的二进制日志中。

故障期间切换源

使用CHANGE MASTER TO语句告诉副本将其更改为新的源 。更改事件的来源不应影响数据库的结构或完整性,因为副本数据库不检查源数据库是否与副本数据库兼容
副本应与该--log-bin选项一起运行 ,如果不使用GTID,则它们也应在不启用log_slave_updates系统变量的情况下运行 。这样,副本就可以准备成为源,而无需重新启动副本mysqld。
未使用log_slave_updates启用副本的原因 是为了防止副本两次接收更新,以防止其中一个副本成为新源。

在每个副本上执行
STOP SLAVE IO_THREAD
SHOW PROCESSLIST
当SHOW PROCESSLI输出Has read all relay log时表示副本已处理玩中继日志中的所有语句

在新源上
STOP SLAVE
RESET MASTER

在其他副本执行
STOP SLAVE
CHANGE MASTER TO MASTER_HOST='Replica1',MASTER_PORT=3306,MASTER_USER='user',MASTER_PASSWORD='password'
无需指定Replica 1二进制日志文件的名称或要读取的日志位置,因为第一个二进制日志文件和位置是默认值。
START SLAVE

使用加密连接复制

若要将加密连接用于复制期间所需的二进制日志的传输,源服务器和副本服务器都必须支持加密网络连接。如果任一服务器都不支持加密连接(因为尚未为其编译或配置),则无法通过加密连接进行复制。
您必须获得(或创建)可以在源上使用的合适的安全证书,并在每个副本上获得相似的证书(来自相同的证书颁发机构)。您还必须获取合适的密钥文件。
要在源上启用加密连接,必须创建或获取合适的证书和密钥文件,然后将以下配置参数添加到[mysqld]源my.cnf文件部分中的源 配置中,并根据需要更改文件名:

[mysqld]
-- 证书颁发机构(CA)证书文件的路径名。(--ssl-capath类似,但指定CA证书文件目录的路径名。)
ssl_ca=cacert.pem
-- 服务器公钥证书文件的路径名。该证书可以发送到客户端,并根据其拥有的CA证书进行身份验证。
ssl_cert=server-cert.pem
-- 服务器私钥文件的路径名。
ssl_key=server-key.pem

要在副本上启用加密连接,可以在副本my.cnf文件中[client]部分命名加密连接所需的副本证书和SSL私钥,也可以使用该CHANGE MASTER TO语句显式指定该信息。
使用选项文件命名副本证书和密钥文件,请将以下行添加到副本my.cnf文件的[client]部分,并 根据需要更改文件名:

[client]
ssl-ca=cacert.pem
ssl-cert=client-cert.pem
ssl-key=client-key.pem

使用--skip-slave-start选项重新启动副本服务器。使用 CHANGE MASTER TO指定的源配置,并添加 MASTER_SSL选项使用加密连接:

mysql> CHANGE MASTER TO
    -> MASTER_HOST='source_hostname',
    -> MASTER_USER='repl',
    -> MASTER_PASSWORD='password',
    -> MASTER_SSL=1;

要使用CHANGE MASTER TO语句命名副本证书和SSL私钥文件,如果未在副本my.cnf文件配置,则需要添加选项: MASTER_SSL_xxx

    -> MASTER_SSL_CA = 'ca_file_name',
    -> MASTER_SSL_CAPATH = 'ca_directory_name',
    -> MASTER_SSL_CERT = 'cert_file_name',
    -> MASTER_SSL_KEY = 'key_file_name',

要激活证书吊销列表(CRL)检查,请添加 MASTER_SSL_CRL或 MASTER_SSL_CRLPATH选项:

    -> MASTER_SSL_CRL = 'crl_file_name',
    -> MASTER_SSL_CRLPATH = 'crl_directory_name',

要指定副本允许复制连接使用的密码和加密协议的列表,请添加 MASTER_SSL_CIPHER和 MASTER_TLS_VERSION选项:

    -> MASTER_SSL_CIPHER = 'cipher_list',
    -> MASTER_TLS_VERSION = 'protocol_list',

该MASTER_SSL_CIPHER选项指定副本允许用于复制连接的密码列表,其中一个或多个密码名称用冒号分隔。该MASTER_TLS_VERSION选项指定副本允许复制连接使用的加密协议。格式类似于tls_version系统变量的格式 ,具有一个或多个逗号分隔的协议版本。在这些列表中可以使用的协议和密码取决于用于编译MySQL的SSL库。

在更新源信息之后,开始复制过程:

mysql> START SLAVE;

在副本服务器上要求加密连接并不能确保源要求副本服务器上的加密连接。如果要确保源仅接受使用加密连接进行连接的副本,请使用REQUIRE SSL选项在源上创建复制用户帐户,然后向该用户授予 REPLICATION SLAVE特权。例如:

mysql> CREATE USER 'repl'@'%.example.com' IDENTIFIED BY 'password'
    -> REQUIRE SSL;
mysql> GRANT REPLICATION SLAVE ON *.*
    -> TO 'repl'@'%.example.com';

如果源上已有复制用户帐户,则可以REQUIRE SSL使用以下语句添加到该帐户:

mysql> ALTER USER 'repl'@'%.example.com' REQUIRE SSL;

复制的问题

基于语句的复制 AUTO_INCREMENT, LAST_INSERT_ID()和 TIMESTAMP值是正确,符合以下情况除外:
在MySQL 5.7.1之前的版本中使用基于语句的复制 AUTO_INCREMENT时,副本上表中的列必须与源上的相同列匹配。
如果源具有字符集与全局character_set_server值不同的数据库 ,则应设计CREATE TABLE语句,以使它们不隐式依赖数据库默认字符集。一个好的解决方法是在CREATE TABLE 语句中显式声明字符集和排序规则。
CREATE DATABASE IF NOT EXISTS无论数据库是否在源上已经存在语句都会被复制。
在MySQL 5.6或更高版本的副本与运行MySQL早期版本的源之间使用复制时,CREATE TABLE ... SELECT导致源上其他表发生更改的 语句在副本上失败,从而导致复制停止。为了防止这种情况的发生,您应该使用基于行的复制,在有问题的源代码上运行之前重写有问题的语句,或者将源代码升级到MySQL 5.7。
当启用二进制日志记录CURRENT_USER()或 CURRENT_USER在其中任何一条语句中使用二进制日志记录时 ,MySQL Server会确保在复制语句时将该语句应用于源和副本服务器上的同一用户。在某些情况下,例如更改密码的语句,在将函数引用写入二进制日志之前会对其进行扩展,以便该语句包含用户名。对于所有其他情况,源上当前用户的名称将作为元数据复制到副本,副本将语句应用于元数据中命名的当前用户,而不是副本上的当前用户。
不支持在分区彼此不同的表之间进行复制。
在源表和目标表没有相同定义的所有情况下,源表和副本表上的数据库名和表名必须相同

{/if}