原文:
dev.mysql.com/doc/refman/8.0/en/replication-semisync-interface.html
当您为半同步复制安装源和复制品插件时(参见 Section 19.4.10.1,“安装半同步复制”),系统变量变得可用以控制插件行为。
要检查半同步复制状态变量的当前值,请使用 SHOW VARIABLES
:
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
从 MySQL 8.0.26 开始,提供了源和复制品插件的新版本,这些版本在系统变量和状态变量中将“master”和“slave”替换为“source”和“replica”。如果安装新的 rpl_semi_sync_source
和 rpl_semi_sync_replica
插件,则新的系统变量和状态变量可用,但旧的则不可用。如果安装旧的 rpl_semi_sync_master
和 rpl_semi_sync_slave
插件,则旧的系统变量和状态变量可用,但新的则不可用。在一个实例中不能同时安装相关插件的新旧版本。
所有 rpl_semi_sync_*
xxx*
系统变量在 Section 19.1.6.2,“复制源选项和变量”和 Section 19.1.6.3,“复制品服务器选项和变量”中有描述。一些关键的系统变量包括:
rpl_semi_sync_source_enabled
或 rpl_semi_sync_master_enabled
控制源服务器上是否启用半同步复制。要启用或禁用插件,请将此变量分别设置为 1 或 0。默认值为 0(关闭)。
rpl_semi_sync_replica_enabled
或 rpl_semi_sync_slave_enabled
控制复制品上是否启用半同步复制。
rpl_semi_sync_source_timeout
或 rpl_semi_sync_master_timeout
以毫秒为单位的值,控制源在提交时等待来自复制品的确认的超时时间,超时后将回滚到异步复制。默认值为 10000(10 秒)。
rpl_semi_sync_source_wait_for_replica_count
或 rpl_semi_sync_master_wait_for_slave_count
控制源在返回会话之前必须接收的每个事务的副本确认数。默认值为 1,意味着源只等待一个副本确认接收事务事件。
rpl_semi_sync_source_wait_point
或 rpl_semi_sync_master_wait_point
系统变量控制半同步源服务器在返回事务提交状态给客户端之前等待副本确认事务接收的时间点。这些值是允许的:
AFTER_SYNC
(默认):源将每个事务写入其二进制日志和副本,并将二进制日志同步到磁盘。源在同步后等待副本确认事务接收。在接收到确认后,源将事务提交到存储引擎并返回结果给客户端,然后客户端可以继续。
AFTER_COMMIT
:源将每个事务写入其二进制日志和副本,同步二进制日志,并提交事务到存储引擎。源在提交后等待副本确认事务接收。在接收到确认后,源返回结果给客户端,然后客户端可以继续。
这些设置的复制特性如下所示:
AFTER_SYNC
,所有客户端在同一时间看到已提交的事务,即在副本确认并在源上提交到存储引擎之后。因此,所有客户端在源上看到相同的数据。
在源故障的情况下,所有在源上提交的事务已被复制到副本(保存到其中继日志)。源的意外退出并故障转移到副本是无损的,因为副本是最新的。如上所述,在故障转移后不应再重用源。
AFTER_COMMIT
,发起事务的客户端只有在服务器提交到存储引擎并接收到副本确认后才会收到返回状态。在提交后和副本确认前,其他客户端可能在提交客户端之前看到已提交的事务。
如果出现问题导致副本未处理事务,那么在源意外退出并故障转移到副本的情况下,这些客户端可能会看到相对于在源上看到的数据有所丢失。
从 MySQL 8.0.23 开始,您可以通过启用系统变量replication_sender_observe_commit_only
和replication_optimize_for_static_plugin_config
来提高半同步复制的性能,前者限制回调,后者添加共享锁并避免不必要的锁获取。这些设置在副本数量增加时非常有帮助,因为锁争用可能会降低性能。半同步复制源服务器还可以通过启用这些系统变量获得性能优势,因为它们使用与副本相同的锁定机制。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-semisync-monitoring.html
半同步复制的插件公开了许多状态变量,使您能够监视其操作。要检查状态变量的当前值,请使用 SHOW STATUS
:
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
从 MySQL 8.0.26 开始,提供了新版本的源和副本插件,这些插件在系统变量和状态变量中用“source”和“replica”替换了“master”和“slave”这些术语。如果安装了新的 rpl_semi_sync_source
和 rpl_semi_sync_replica
插件,则新的系统变量和状态变量可用,而旧的则不可用。如果安装了旧的 rpl_semi_sync_master
和 rpl_semi_sync_slave
插件,则旧的系统变量和状态变量可用,而新的则不可用。在一个实例中不能同时安装相关插件的新旧版本。
所有 Rpl_semi_sync_*
xxx*
状态变量在 第 7.1.10 节,“服务器状态变量” 中有描述。一些示例包括:
Rpl_semi_sync_source_clients
或 Rpl_semi_sync_master_clients
连接到源服务器的半同步副本数量。
Rpl_semi_sync_source_status
或 Rpl_semi_sync_master_status
半同步复制当前是否在源服务器上运行。如果插件已启用且提交确认尚未发生,则值为 1。如果插件未启用或源由于提交确认超时而回退到异步复制,则值为 0。
Rpl_semi_sync_source_no_tx
或 Rpl_semi_sync_master_no_tx
未被副本成功确认的提交数量。
Rpl_semi_sync_source_yes_tx
或 Rpl_semi_sync_master_yes_tx
被副本成功确认的提交数量。
Rpl_semi_sync_replica_status
或 Rpl_semi_sync_slave_status
半同步复制当前是否在副本上运行。如果插件已启用且复制 I/O(接收器)线程正在运行,则为 1,否则为 0。
当源端由于提交阻塞超时或副本追赶而在异步或半同步复制之间切换时,它会适当地设置Rpl_semi_sync_source_status
或Rpl_semi_sync_master_status
状态变量的值。源端从半同步自动回退到异步复制意味着即使半同步复制实际上当前不可用,rpl_semi_sync_source_enabled
或rpl_semi_sync_master_enabled
系统变量在源端可能仍然具有值 1。您可以监视Rpl_semi_sync_source_status
或Rpl_semi_sync_master_status
状态变量来确定源端当前是在使用异步还是半同步复制。
MySQL 支持延迟复制,使得副本服务器故意比源服务器晚至少指定的时间执行事务。本节描述了如何在副本上配置复制延迟以及如何监视复制延迟。
在 MySQL 8.0 中,延迟复制的方法取决于两个时间戳,immediate_commit_timestamp
和original_commit_timestamp
(参见 Replication Delay Timestamps)。如果复制拓扑中的所有服务器都运行 MySQL 8.0 或更高版本,则使用这些时间戳来测量延迟复制。如果即时源或副本中有任何一个不使用这些时间戳,则使用 MySQL 5.7 中的延迟复制实现(参见 Delayed Replication)。本节描述了所有使用这些时间戳的服务器之间的延迟复制。
默认复制延迟为 0 秒。使用CHANGE REPLICATION SOURCE TO SOURCE_DELAY=*
N*
语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO MASTER_DELAY=*
N*
语句(MySQL 8.0.23 之前)将延迟设置为*N
秒。从源接收的事务直到比其在即时源上提交的时间晚至少N
*秒才执行。延迟是按事务发生的(而不是像以前的 MySQL 版本中那样按事件发生),实际延迟仅对gtid_log_event
或anonymous_gtid_log_event
施加。事务中的其他事件总是在这些事件之后立即执行,而不会对它们施加任何等待时间。
注意
START REPLICA
和STOP REPLICA
立即生效并忽略任何延迟。RESET REPLICA
将延迟重置为 0。
replication_applier_configuration
性能模式表包含DESIRED_DELAY
列,显示使用SOURCE_DELAY
| MASTER_DELAY
选项配置的延迟。replication_applier_status
性能模式表包含REMAINING_DELAY
列,显示剩余的延迟秒数。
延迟复制可用于几个目的:
MySQL 8.0 提供了一种新的方法来测量复制拓扑中的延迟(也称为复制滞后),该方法依赖于写入二进制日志的每个事务(而不是每个事件)关联的 GTID 的以下时间戳。
original_commit_timestamp
: 事务被写入(提交)到原始来源二进制日志时距离时代的微秒数。
immediate_commit_timestamp
: 事务被写入(提交)到直接来源二进制日志时距离时代的微秒数。
mysqlbinlog的输出以两种格式显示这些时间戳,即从时代开始的微秒数和基于用户定义时区的TIMESTAMP
格式,以便更易读。例如:
#170404 10:48:05 server id 1 end_log_pos 233 CRC32 0x016ce647 GTID last_committed=0
\ sequence_number=1 original_committed_timestamp=1491299285661130 immediate_commit_timestamp=1491299285843771
# original_commit_timestamp=1491299285661130 (2017-04-04 10:48:05.661130 WEST)
# immediate_commit_timestamp=1491299285843771 (2017-04-04 10:48:05.843771 WEST)
/*!80001 SET @@SESSION.original_commit_timestamp=1491299285661130*//*!*/;
SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'/*!*/;
# at 233
通常情况下,事务应用到的所有副本上的original_commit_timestamp
始终相同。在源-副本复制中,事务在(原始)源二进制日志中的original_commit_timestamp
始终与其immediate_commit_timestamp
相同。在副本的中继日志中,事务的original_commit_timestamp
和immediate_commit_timestamp
与源二进制日志中的相同;而在其自己的二进制日志中,事务的immediate_commit_timestamp
对应于副本提交事务的时间。
在群组复制设置中,当原始来源是群组的成员时,original_commit_timestamp
在事务准备提交时生成。换句话说,当它在原始来源上执行完毕并且其写入集准备发送到群组的所有成员进行认证时。当原始来源是群组外的服务器时,original_commit_timestamp
被保留。特定事务的相同original_commit_timestamp
被复制到群组中的所有服务器,以及从成员复制的群组外的任何副本。从 MySQL 8.0.26 开始,事务的每个接收者还使用immediate_commit_timestamp
在其二进制日志中存储本地提交时间。
视图更改事件,这是组复制独有的特殊情况。包含这些事件的交易由每个组成员生成,但共享相同的 GTID(因此,它们不是首先在源中执行,然后被复制到组中,而是组的所有成员执行并应用相同的交易)。在 MySQL 8.0.26 之前,这些交易的original_commit_timestamp
设置为零,并且在可查看的输出中以这种方式显示。从 MySQL 8.0.26 开始,为了提高可观察性,组成员为与视图更改事件相关的交易设置本地时间戳值。
在以前的 MySQL 版本中监视复制延迟(滞后)最常见的方法之一是依赖于SHOW REPLICA STATUS
输出中的Seconds_Behind_Master
字段。然而,在使用比传统源-副本设置更复杂的复制拓扑时,如组复制时,此度量标准并不适用。在 MySQL 8 中添加immediate_commit_timestamp
和original_commit_timestamp
提供了关于复制延迟的更精细信息。在支持这些时间戳的拓扑中监视复制延迟的推荐方法是使用以下性能模式表。
replication_connection_status
:与源的连接的当前状态,提供连接线程排队到中继日志的最后一个和当前交易的信息。
replication_applier_status_by_coordinator
:仅在使用多线程副本时显示协调器线程的当前状态,提供协调器线程缓冲到工作线程队列的最后一个交易的信息,以及它当前正在缓冲的交易。
replication_applier_status_by_worker
:应用来自源的交易的线程(们)的当前状态,提供由复制 SQL 线程应用的交易的信息,或者在使用多线程副本时,每个工作线程应用的交易的信息。
使用这些表,您可以监视对应线程处理的最后一个交易和该线程当前正在处理的交易的信息。这些信息包括:
original_commit_timestamp
和immediate_commit_timestamp
除了性能模式表之外,SHOW REPLICA STATUS
的输出还有三个字段显示:
SQL_Delay
: 一个非负整数,表示使用CHANGE REPLICATION SOURCE TO SOURCE_DELAY=*
N*
(从 MySQL 8.0.23 开始)或CHANGE MASTER TO MASTER_DELAY=N
(在 MySQL 8.0.23 之前)配置的复制延迟,以秒为单位。
SQL_Remaining_Delay
: 当Replica_SQL_Running_State
为Waiting until MASTER_DELAY seconds after master executed event
时,此字段包含一个整数,表示延迟剩余的秒数。在其他时候,此字段为NULL
。
Replica_SQL_Running_State
: 表示 SQL 线程状态的字符串(类似于Replica_IO_State
)。该值与由SHOW PROCESSLIST
显示的 SQL 线程的State
值相同。
当复制 SQL 线程在执行事件之前等待延迟时间时,SHOW PROCESSLIST
将其State
值显示为Waiting until MASTER_DELAY seconds after master executed event
。
19.5.1 复制功能和问题
19.5.2 MySQL 版本之间的复制兼容性
19.5.3 升级复制拓扑结构
19.5.4 复制故障排除
19.5.5 如何报告复制错误或问题
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features.html
19.5.1.1 复制和 AUTO_INCREMENT
19.5.1.2 复制和 BLACKHOLE 表
19.5.1.3 复制和字符集
19.5.1.4 复制和 CHECKSUM TABLE
19.5.1.5 创建服务器,修改服务器和删除服务器的复制
19.5.1.6 CREATE … IF NOT EXISTS 语句的复制
19.5.1.7 复制 CREATE TABLE … SELECT 语句
19.5.1.8 CURRENT_USER()的复制
19.5.1.9 源和副本上的不同表定义的复制
19.5.1.10 复制和 DIRECTORY 表选项
19.5.1.11 复制 DROP … IF EXISTS 语句
19.5.1.12 复制和浮点值
19.5.1.13 复制和 FLUSH
19.5.1.14 复制和系统函数
19.5.1.15 复制和分数秒支持
19.5.1.16 调用功能的复制
19.5.1.17 复制 JSON 文档
19.5.1.18 复制和 LIMIT
19.5.1.19 复制和 LOAD DATA
19.5.1.20 复制和 max_allowed_packet
19.5.1.21 复制和 MEMORY 表
19.5.1.22 复制 mysql 系统模式
19.5.1.23 复制和查询优化器
19.5.1.24 复制和分区
19.5.1.25 复制和 REPAIR TABLE
19.5.1.26 复制和保留字
19.5.1.27 复制和行搜索
19.5.1.28 复制和源或副本的关闭
19.5.1.29 复制过程中的副本错误
19.5.1.30 复制和服务器 SQL 模式
19.5.1.31 复制和临时表
19.5.1.32 复制重试和超时
19.5.1.33 复制和时区
19.5.1.34 复制和事务不一致性
19.5.1.35 复制和事务
19.5.1.36 复制和触发器
19.5.1.37 复制和 TRUNCATE TABLE
19.5.1.38 复制和用户名长度
19.5.1.39 复制和变量
19.5.1.40 复制和视图
以下各节提供了关于 MySQL 复制中支持和不支持的内容,以及在复制某些语句时可能发生的特定问题和情况的信息。
基于语句的复制取决于源和副本之间在 SQL 级别的兼容性。换句话说,成功的基于语句的复制要求源服务器和副本服务器都支持使用的任何 SQL 特性。如果您在源服务器上使用了仅在当前版本的 MySQL 中可用的功能,则无法复制到使用较早版本的 MySQL 的副本。这种不兼容性也可能发生在版本系列内以及版本之间。
如果您计划在 MySQL 8.0 和之前的 MySQL 版本系列之间使用基于语句的复制,建议查阅MySQL 参考手册对应早期版本系列的版本,以获取有关该系列复制特性的信息。
使用 MySQL 的基于语句的复制时,可能会出现复制存储过程或触发器的问题。您可以通过改用 MySQL 的基于行的复制来避免这些问题。有关问题的详细列表,请参阅第 27.7 节,“存储程序二进制日志记录”。有关基于行的日志记录和基于行的复制的更多信息,请参阅第 7.4.4.1 节,“二进制日志格式”,以及第 19.2.1 节,“复制格式”。
对于与InnoDB
复制相关的其他信息,请参阅第 17.19 节,“InnoDB 和 MySQL 复制”。有关与 NDB Cluster 复制相关的信息,请参阅第 25.7 节,“NDB Cluster 复制”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-auto-increment.html
基于语句的复制AUTO_INCREMENT
、LAST_INSERT_ID()
和TIMESTAMP
值的执行受以下异常情况的影响:
调用触发器或导致AUTO_INCREMENT
列更新的函数的语句在基于语句的复制中无法正确复制。这些语句被标记为不安全。(Bug #45677)
对于具有包含AUTO_INCREMENT
列但不是这个复合主键的第一列的复合主键的表进行INSERT
不适用于基于语句的日志记录或复制。这些语句被标记为不安全。(Bug #11754117, Bug #45670)
这个问题不影响使用InnoDB
存储引擎的表,因为InnoDB
表的AUTO_INCREMENT
列需要至少一个键,其中AUTO_INCREMENT
列是唯一的或最左边的列。
向具有ALTER TABLE
的表添加AUTO_INCREMENT
列可能不会在副本和源上产生相同的行顺序。这是因为行编号的顺序取决于用于表的特定存储引擎以及插入行的顺序。如果在源和副本上具有相同的顺序很重要,则在分配AUTO_INCREMENT
编号之前必须对行进行排序。假设您想向具有列col1
和col2
的表t1
添加AUTO_INCREMENT
列,以下语句将生成一个与t1
相同但具有AUTO_INCREMENT
列的新表t2
:
CREATE TABLE t2 LIKE t1;
ALTER TABLE t2 ADD id INT AUTO_INCREMENT PRIMARY KEY;
INSERT INTO t2 SELECT * FROM t1 ORDER BY col1, col2;
重要提示
为了保证源和副本的顺序相同,ORDER BY
子句必须命名t1
的所有列。
刚刚给出的指令受到CREATE TABLE ... LIKE
的限制:外键定义被忽略,DATA DIRECTORY
和INDEX DIRECTORY
表选项也被忽略。如果表定义包含任何这些特征,使用与创建t1
相同的CREATE TABLE
语句创建t2
,但增加AUTO_INCREMENT
列。
无论用于创建和填充具有AUTO_INCREMENT
列的副本的方法如何,最后一步是删除原始表,然后重命名副本:
DROP t1;
ALTER TABLE t2 RENAME t1;
另请参阅 Section B.3.6.1, “Problems with ALTER TABLE”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-blackhole.html
BLACKHOLE
存储引擎接受数据但会丢弃它,不会存储。在执行二进制日志记录时,所有对这些表的插入操作都会被记录,无论使用的日志格式是什么。根据使用的基于语句或基于行的日志记录方式,更新和删除操作会有不同的处理方式。在基于语句的日志记录格式下,所有影响BLACKHOLE
表的语句都会被记录,但它们的效果会被忽略。在使用基于行的日志记录时,对这些表的更新和删除操作会被简单地跳过,不会被写入二进制日志。每当发生这种情况时,都会记录一个警告。
出于这个原因,我们建议当您使用BLACKHOLE
存储引擎复制表时,将binlog_format
服务器变量设置为STATEMENT
,而不是ROW
或MIXED
。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-charset.html
在使用不同字符集的 MySQL 服务器之间进行复制时,以下内容适用:
character_set_server
值不同的字符集,则应设计CREATE TABLE
语句,使其不会隐式依赖于数据库默认字符集。一个好的解决方法是在CREATE TABLE
语句中明确指定字符集和校对规则。原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-checksum-table.html
CHECKSUM TABLE
返回一个逐行计算的校验和,使用依赖于表行存储格式的方法。存储格式在 MySQL 版本之间不保证保持不变,因此在升级后校验和值可能会改变。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-create-alter-drop-server.html
CREATE SERVER
、ALTER SERVER
和DROP SERVER
CREATE SERVER
、ALTER SERVER
和DROP SERVER
语句不管使用何种二进制日志格式,都不会被写入二进制日志中。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-create-if-not-exists.html
CREATE ... IF NOT EXISTS
语句当复制各种CREATE ... IF NOT EXISTS
语句时,MySQL 会应用以下规则:
CREATE DATABASE IF NOT EXISTS
语句都会被复制,无论源数据库是否已存在。
SELECT
的CREATE TABLE IF NOT EXISTS
语句都会被复制,无论源端表是否已存在。这包括CREATE TABLE IF NOT EXISTS ... LIKE
。CREATE TABLE IF NOT EXISTS ... SELECT
的复制遵循稍有不同的规则;更多信息请参见第 19.5.1.7 节,“复制CREATE TABLE ... SELECT
语句”。
CREATE EVENT IF NOT EXISTS
始终会被复制,无论语句中指定的事件是否已存在于源端。
CREATE USER
语句才会被写入二进制日志。如果语句包含IF NOT EXISTS
,则被视为成功,并在至少创建一个在语句中命名的用户时记录;在这种情况下,语句将被记录为原样;这包括对未创建的现有用户的引用。更多信息请参见创建用户二进制日志记录。
CREATE PROCEDURE IF NOT EXISTS
、CREATE FUNCTION IF NOT EXISTS
或CREATE TRIGGER IF NOT EXISTS
,如果成功,将完整写入二进制日志(包括IF NOT EXISTS
子句),无论语句是否因对象(过程、函数或触发器)已存在而引发警告。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-create-select.html
当复制CREATE TABLE ... SELECT
语句时,MySQL 应用以下规则:
CREATE TABLE ... SELECT
总是执行隐式提交(第 15.3.3 节,“导致隐式提交的语句”)。
IF NOT EXISTS
都无关紧要。
STATEMENT
或MIXED
格式:该语句按原样记录。
ROW
格式:该语句被记录为一个CREATE TABLE
语句,后跟一系列插入行事件。
在 MySQL 8.0.21 之前,该语句被记录为两个事务。从 MySQL 8.0.21 开始,在支持原子 DDL 的存储引擎上,它被记录为一个事务。更多信息,请参阅第 15.1.1 节,“原子数据定义语句支持”。
CREATE TABLE ... SELECT
语句失败,则不会记录任何内容。这包括目标表存在且未使用IF NOT EXISTS
的情况。
IF NOT EXISTS
,MySQL 8.0 会完全忽略该语句;不会插入任何内容或记录日志。
MySQL 8.0 不允许CREATE TABLE ... SELECT
语句对除了该语句创建的表之外的其他表进行任何更改。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-current-user.html
以下语句支持使用CURRENT_USER()
函数来代替受影响用户或定义者的名称,以及可能的主机:
DROP USER
RENAME USER
GRANT
REVOKE
CREATE FUNCTION
CREATE PROCEDURE
CREATE TRIGGER
CREATE EVENT
CREATE VIEW
ALTER EVENT
ALTER VIEW
SET PASSWORD
当启用二进制日志记录并且在这些语句中使用CURRENT_USER()
或CURRENT_USER
作为定义者时,MySQL 服务器确保在语句被复制时应用于源和副本上的相同用户。在某些情况下,例如更改密码的语句,函数引用在写入二进制日志之前会被展开,以便语句包含用户名称。对于所有其他情况,源上的当前用户名称会作为元数据被复制到副本上,并且副本会将语句应用于元数据中命名的当前用户,而不是副本上的当前用户。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-differing-tables.html
源表和目标表的复制不必完全相同。源表可以比副本表的副本具有更多或更少的列。此外,源表和副本的对应表列可以使用不同的数据类型,但必须满足一定条件。
注意
不支持不同分区的表之间的复制。参见 Section 19.5.1.24, “复制和分区”。
在所有源表和目标表定义不完全相同的情况下,数据库和表名必须在源表和副本上相同。在以下两个部分中讨论了其他条件,并给出了示例。
可以将表从源复制到副本,使得源表和副本表的列数不同,但必须满足以下条件:
两个表共有的列必须在源表和副本上以相同顺序定义。(即使两个表具有相同数量的列也是如此。)
两个表共有的列必须在任何额外列之前定义。
这意味着在副本上执行ALTER TABLE
语句,向表中插入一个新列,该列位于两个表共有的列范围内,会导致复制失败,如下例所示:
假设表t
在源和副本上存在,并由以下CREATE TABLE
语句定义:
CREATE TABLE t (
c1 INT,
c2 INT,
c3 INT
);
假设在副本上执行了以下ALTER TABLE
语句:
ALTER TABLE t ADD COLUMN cnew1 INT AFTER c3;
之前的ALTER TABLE
在副本上是允许的,因为表t
的两个版本中共有的列c1
、c2
和c3
在任何不同的列之前都保持在一起。
然而,在副本上执行以下ALTER TABLE
语句会导致复制中断:
ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;
在副本上执行刚刚显示的ALTER TABLE
语句后,复制失败,因为新列cnew2
位于t
两个版本共有的列之间。
在列更多的表版本中,每个“额外”列必须有一个默认值。
列的默认值由多种因素决定,包括其类型、是否使用DEFAULT
选项定义、是否声明为NULL
,以及创建时服务器 SQL 模式的有效性;更多信息,请参见第 13.6 节,“数据类型默认值”)。
此外,当副本表比源表具有更多列时,两个表中共有的每一列必须在两个表中使用相同的数据类型。
示例。 以下示例说明了一些有效和无效的表定义:
源表中有更多列。 以下表定义是有效的,并且可以正确复制:
source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica> CREATE TABLE t1 (c1 INT, c2 INT);
下表定义会引发错误,因为两个版本表共有的列的定义在副本表上的顺序与源表上的顺序不同:
source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica> CREATE TABLE t1 (c2 INT, c1 INT);
下表定义也会引发错误,因为源表中额外列的定义出现在两个版本共有的列定义之前:
source> CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
replica> CREATE TABLE t1 (c1 INT, c2 INT);
副本表中有更多列。 以下表定义是有效的,并且可以正确复制:
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
下列定义会引发错误,因为两个版本表共有的列在源表和副本表上的定义顺序不同:
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica> CREATE TABLE t1 (c2 INT, c1 INT, c3 INT);
下表定义也会引发错误,因为副本版本表中额外列的定义出现在两个版本共有的列定义之前:
source> CREATE TABLE t1 (c1 INT, c2 INT);
replica> CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
下表定义失败,因为副本版本表比源版本表多了额外列,并且两个版本的表对于共有列c2
使用了不同的数据类型:
source> CREATE TABLE t1 (c1 INT, c2 BIGINT);
replica> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
源表和副本表中相应的列的副本应该具有相同的数据类型。然而,并不总是严格执行这一规定,只要满足一定条件即可。
通常可以从具有特定数据类型的列复制到具有相同类型和大小或宽度的另一列,如适用,或更大的列。例如,可以从CHAR(10)
列复制到另一个CHAR(10)
列,或者从CHAR(10)
列复制到CHAR(25)
列而不会出现问题。在某些情况下,还可以从源表中具有一种数据类型的列复制到副本中具有不同数据类型的列;当源表中列的数据类型提升为副本中相同大小或更大的类型时,这称为属性提升。
属性提升可用于基于语句和基于行的复制,并且不依赖于源或副本使用的存储引擎。但是,日志格式的选择确实会影响允许的类型转换;具体内容将在本节后面讨论。
重要提示
无论您使用基于语句还是基于行的复制,如果希望使用属性提升,则副本表的副本不能包含比源表的副本更多的列。
基于语句的复制。 在使用基于语句的复制时,一个简单的经验法则是,“如果在源上运行的语句也可以在副本上成功执行,则它也应该成功复制”。换句话说,如果语句使用与副本上给定列类型兼容的值,则可以复制该语句。例如,您可以将适合TINYINT
列的任何值插入到BIGINT
列中;因此,即使您将副本表中的TINYINT
列的类型更改为BIGINT
,任何成功插入该列的源上的插入也应该在副本上成功,因为不可能有一个合法的TINYINT
值大到足以超过BIGINT
列。
基于行的复制:属性提升和降级。 基于行的复制支持较小数据类型和较大类型之间的属性提升和降级。还可以指定是否允许对降级列值进行有损(截断)或无损转换,如本节后面所述。
有损和无损转换。 在目标类型无法表示要插入的值的情况下,必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现“适合”,我们进行的是所谓的有损转换。不需要截断或类似修改以使源列值适合目标列的转换是无损转换。
类型转换模式。 系统变量replica_type_conversions
(从 MySQL 8.0.26 开始)或slave_type_conversions
(在 MySQL 8.0.26 之前)的全局值控制副本上使用的类型转换模式。此变量接受以下列表中的一组值,描述了每种模式对副本的类型转换行为的影响:
ALL_LOSSY
在此模式下,允许会导致信息丢失的类型转换。
这并不意味着允许非损失转换,仅表示只允许需要损失转换或根本不需要转换的情况;例如,仅启用此模式允许将INT
列转换为TINYINT
(损失转换),但不允许将TINYINT
列转换为INT
列(非损失)。在这种情况下尝试后者的转换会导致副本停止并显示错误。
ALL_NON_LOSSY
此模式允许不需要截断或其他特殊处理源值的转换;也就是说,它允许目标类型的范围比源类型更宽的转换。
设置此模式不影响是否允许有损转换;这由ALL_LOSSY
模式控制。如果只设置了ALL_NON_LOSSY
,但没有设置ALL_LOSSY
,那么尝试进行可能导致数据丢失的转换(例如INT
到TINYINT
,或CHAR(25)
到VARCHAR(20)
)会导致副本停止并显示错误。
ALL_LOSSY,ALL_NON_LOSSY
当设置此模式时,允许所有支持的类型转换,无论它们是否是有损转换。
ALL_SIGNED
将提升的整数类型视为有符号值(默认行为)。
ALL_UNSIGNED
将提升的整数类型视为无符号值。
ALL_SIGNED,ALL_UNSIGNED
尽可能将提升的整数类型视为有符号,否则视为无符号。
[空]
当未设置replica_type_conversions
或slave_type_conversions
时,不允许进行属性提升或降级;这意味着源表和目标表中的所有列必须是相同类型。
这是默认模式。
当整数类型被提升时,其符号性不会被保留。默认情况下,副本将所有这些值视为有符号的。您可以使用ALL_SIGNED
、ALL_UNSIGNED
或两者来控制此行为。ALL_SIGNED
告诉副本将所有提升的整数类型视为有符号;ALL_UNSIGNED
指示将其视为无符号。如果同时指定两者,副本将尽可能将值视为有符号,否则将视为无符号;它们列出的顺序并不重要。如果至少没有使用ALL_LOSSY
或ALL_NONLOSSY
中的一个,那么ALL_SIGNED
和ALL_UNSIGNED
都不会产生任何效果。
更改类型转换模式需要使用新的replica_type_conversions
或slave_type_conversions
设置重新启动副本。
支持的转换。 支持不同但相似数据类型之间的转换列在以下列表中:
TINYINT
,SMALLINT
,MEDIUMINT
,INT
和BIGINT
之间。
这包括这些类型的有符号和无符号版本之间的转换。
通过将源值截断为目标列允许的最大(或最小)值来进行有损转换。为了确保从无符号到有符号类型的非有损转换,目标列必须足够大,以容纳源列中的值范围。例如,您可以将TINYINT UNSIGNED
非有损地降级为SMALLINT
,但不能降级为TINYINT
。
DECIMAL
,FLOAT
,DOUBLE
和NUMERIC
之间。
FLOAT
到DOUBLE
是非有损转换;DOUBLE
到FLOAT
只能以有损方式处理。从DECIMAL(*
M*,*
D*)
到DECIMAL(*
M’*,*
D’*)
的转换,其中*
D’* >= *
D*
和(*
M’*-*
D’*) >= (*
M*-*
D*
)是非有损的;对于任何*
M’* < *
M*
,*
D’* < *
D*
,或两者都是的情况,只能进行有损转换。
对于任何十进制类型,如果要存储的值无法适应目标类型,则根据文档中其他地方定义的服务器舍入规则将该值向下舍入。有关十进制类型的此类操作的详细信息,请参见第 14.24.4 节,“舍入行为”。
CHAR
,VARCHAR
和TEXT
之间,包括不同宽度之间的转换。
将CHAR
,VARCHAR
或TEXT
转换为大小相同或更大的CHAR
,VARCHAR
或TEXT
列永远不会有损。有损转换通过在副本上仅插入字符串的前*N
个字符来处理,其中N
*是目标列的宽度。
重要提示
不支持在使用不同字符集的列之间进行复制。
BINARY
,VARBINARY
和BLOB
之间的转换,包括不同宽度之间的转换。
将BINARY
,VARBINARY
或BLOB
转换为相同大小或更大的BINARY
,VARBINARY
或BLOB
列永远不会有损失。有损转换通过仅在副本上插入字符串的前*N
字节来处理,其中N
*是目标列的宽度。
BIT
列之间。
当将来自BIT(*
M*)
列的值插入到BIT(*
M’*)
列中,其中*
M’* > *
M*
时,BIT(*
M’*)
列的最高有效位被清除(设置为零),而BIT(*
M*)
值的*M
*位被设置为BIT(*
M’*)
列的最低有效位。
当将来自源BIT(*
M*)
列的值插入到目标BIT(*
M’*)
列中,其中*
M’* < *
M*
时,BIT(*
M’*)
列被赋予最大可能的值;换句话说,目标列被赋予“全置位”值。
类型转换不在前述列表中的类型是不允许的。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-directory.html
如果在源服务器的CREATE TABLE
语句中使用了DATA DIRECTORY
或INDEX DIRECTORY
表选项,则该表选项也会在副本上使用。如果在副本主机文件系统中不存在相应的目录,或者存在但对副本 MySQL 服务器不可访问,则可能会出现问题。可以通过在副本上使用NO_DIR_IN_CREATE
服务器 SQL 模式来覆盖此行为,这会导致副本在复制CREATE TABLE
语句时忽略DATA DIRECTORY
和INDEX DIRECTORY
表选项。结果是MyISAM
数据和索引文件将在表的数据库目录中创建。
查看更多信息,请参见第 7.1.11 节,“服务器 SQL 模式”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-drop-if-exists.html
DROP ... IF EXISTS
语句DROP DATABASE IF EXISTS
,DROP TABLE IF EXISTS
和DROP VIEW IF EXISTS
语句始终会被复制,即使要删除的数据库、表或视图在源上不存在。这是为了确保一旦副本赶上源,要删除的对象在源或副本上都不再存在。
DROP ... IF EXISTS
语句用于存储程序(存储过程和函数,触发器和事件),即使要删除的存储程序在源上不存在,也会被复制。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-floatvalues.html
在基于语句的复制中,值从十进制转换为二进制。由于十进制和二进制表示之间的转换可能是近似的,涉及浮点值的比较是不精确的。这对明确使用浮点值的操作,或者隐式转换为浮点的值都是适用的。由于计算机架构、用于构建 MySQL 的编译器等方面的差异,源服务器和副本服务器上对浮点值的比较可能产生不同的结果。参见第 14.3 节,“表达式评估中的类型转换”,以及第 B.3.4.8 节,“浮点值的问题”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-flush.html
一些形式的FLUSH
语句不会被记录,因为如果被复制到副本中可能会引起问题:FLUSH LOGS
和FLUSH TABLES WITH READ LOCK
。有关语法示例,请参见 Section 15.7.8.3, “FLUSH Statement”。FLUSH TABLES
、ANALYZE TABLE
、OPTIMIZE TABLE
和REPAIR TABLE
语句会被写入二进制日志,因此会被复制到副本中。通常这不是问题,因为这些语句不会修改表数据。
然而,在某些情况下,这种行为可能会引起困难。如果在mysql
数据库中复制权限表并直接更新这些表而不使用GRANT
,则必须在副本上发出FLUSH PRIVILEGES
以使新权限生效。此外,如果在重命名作为MERGE
表的MyISAM
表时使用FLUSH TABLES
,则必须在副本上手动发出FLUSH TABLES
。除非指定NO_WRITE_TO_BINLOG
或其别名LOCAL
,否则这些语句将被写入二进制日志。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-functions.html
在某些情况下,某些函数无法很好地进行复制:
USER()
、CURRENT_USER()
(或CURRENT_USER
)、UUID()
、VERSION()
和LOAD_FILE()
函数在复制时不会更改,因此在副本上无法可靠工作,除非启用了基于行的复制。(参见 Section 19.2.1, “复制格式”。)
在MIXED
模式下使用基于行的复制时,USER()
和CURRENT_USER()
会自动复制,并在STATEMENT
模式下生成警告。(另请参阅 Section 19.5.1.8, “CURRENT_USER()的复制”的复制")。)对于VERSION()
和RAND()
也是如此。
对于NOW()
,二进制日志包括时间戳。这意味着源上调用此函数返回的值会被复制到副本中。为了避免在不同时区的 MySQL 服务器之间复制时出现意外结果,请在源和副本上都设置时区。更多信息,请参见 Section 19.5.1.33, “复制和时区”。
为了解释在不同时区的服务器之间复制时可能出现的问题,假设源位于纽约,副本位于斯德哥尔摩,两台服务器都使用当地时间。进一步假设,在源上,你创建了一个表mytable
,对该表执行了一个INSERT
语句,然后从表中选择,如下所示:
mysql> CREATE TABLE mytable (mycol TEXT);
Query OK, 0 rows affected (0.06 sec)
mysql> INSERT INTO mytable VALUES ( NOW() );
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM mytable;
+---------------------+
| mycol |
+---------------------+
| 2009-09-01 12:00:00 |
+---------------------+
1 row in set (0.00 sec)
斯德哥尔摩的当地时间比纽约晚 6 小时;因此,如果你在副本上的确切时刻发出SELECT NOW()
,将返回值2009-09-01 18:00:00
。因此,如果在复制了刚刚显示的CREATE TABLE
和INSERT
语句之后,从副本的mytable
复制,你可能期望mycol
包含值2009-09-01 18:00:00
。然而,事实并非如此;当你从副本的mytable
复制时,你得到的结果与源上完全相同:
mysql> SELECT * FROM mytable;
+---------------------+
| mycol |
+---------------------+
| 2009-09-01 12:00:00 |
+---------------------+
1 row in set (0.00 sec)
与NOW()
不同,SYSDATE()
函数不是复制安全的,因为它不受二进制日志中SET TIMESTAMP
语句的影响,并且在使用基于语句的日志记录时是不确定的。如果使用基于行的日志记录,则不会出现问题。
另一种选择是使用--sysdate-is-now
选项,使SYSDATE()
成为NOW()
的别名。这必须在源和副本上都执行才能正常工作。在这种情况下,此函数仍会发出警告,但只要在源和副本上都使用--sysdate-is-now
,就可以安全地忽略。
当使用MIXED
模式时,SYSDATE()
会自动使用基于行的复制进行复制,并在STATEMENT
模式下生成警告。
另请参阅 Section 19.5.1.33, “Replication and Time Zones”。
以下限制仅适用于基于语句的复制,不适用于基于行的复制。 处理用户级锁的GET_LOCK()
、RELEASE_LOCK()
、IS_FREE_LOCK()
和IS_USED_LOCK()
函数在源上处理并在副本上不知道并发上下文。因此,这些函数不应用于插入源表,因为副本上的内容会有所不同。例如,不要发出类似INSERT INTO mytable VALUES(GET_LOCK(...))
的语句。
当使用MIXED
模式时,这些函数会自动使用基于行的复制进行复制,并在STATEMENT
模式下生成警告。
作为解决方案,当基于语句的复制生效时,您可以使用将有问题的函数结果保存在用户变量中,并在后续语句中引用该变量的策略。例如,以下单行INSERT
由于引用UUID()
函数而存在问题:
INSERT INTO t VALUES(UUID());
要解决这个问题,可以这样做��
SET @my_uuid = UUID();
INSERT INTO t VALUES(@my_uuid);
该序列的语句之所以能够复制,是因为@my_uuid
的值作为用户变量事件存储在二进制日志中,先于INSERT
语句,并且可以在INSERT
中使用。
相同的思路也适用于多行插入,但使用起来更加繁琐。对于两行插入,您可以这样做:
SET @my_uuid1 = UUID(); @my_uuid2 = UUID();
INSERT INTO t VALUES(@my_uuid1),(@my_uuid2);
但是,如果行数很大或者未知,解决方法就会变得困难或者不可行。例如,你无法将以下语句转换为一个语句,其中一个给定的个人用户变量与每一行相关联:
INSERT INTO t2 SELECT UUID(), * FROM t1;
在存储函数中,RAND()
只要在函数执行过程中仅调用一次,就能正确复制。(你可以将函数执行时间戳和随机数种子视为在源和副本上相同的隐式输入。)
FOUND_ROWS()
和 ROW_COUNT()
函数在基于语句的复制中无法可靠复制。一个解决方法是将函数调用的结果存储在用户变量中,然后在 INSERT
语句中使用。例如,如果你希望将结果存储在名为 mytable
的表中,你可能会像这样做:
SELECT SQL_CALC_FOUND_ROWS FROM mytable LIMIT 1;
INSERT INTO mytable VALUES( FOUND_ROWS() );
但是,如果你正在复制 mytable
,你应该使用 SELECT ... INTO
,然后将变量存储在表中,就像这样:
SELECT SQL_CALC_FOUND_ROWS INTO @found_rows FROM mytable LIMIT 1;
INSERT INTO mytable VALUES(@found_rows);
这样,用户变量将作为上下文的一部分进行复制,并在副本上正确应用。
当使用 MIXED
模式时,这些函数会在基于行的复制中自动复制,并在 STATEMENT
模式下生成警告。(Bug #12092, Bug #30244)
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-fractional-seconds.html
MySQL 8.0 允许TIME
、DATETIME
和TIMESTAMP
值使用微秒(6 位数字)精度的分数秒。请参见第 13.2.6 节,“时间值中的分数秒”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-invoked.html
调用特性的复制,如可加载函数和存储程序(存储过程和函数、触发器和事件),提供以下特性:
CREATE EVENT
ALTER EVENT
DROP EVENT
CREATE PROCEDURE
DROP PROCEDURE
CREATE FUNCTION
DROP FUNCTION
CREATE TRIGGER
DROP TRIGGER
但是,使用这些语句创建、修改或删除的特性的效果是使用基于行的复制进行复制的。 注意 尝试使用基于语句的复制复制调用特性会产生警告“Statement is not safe to log in statement format”。例如,尝试使用基于语句的复制复制可加载函数会生成此警告,因为当前无法由 MySQL 服务器确定函数是否是确定性的。如果您绝对确定调用特性的效果是确定性的,可以安全地忽略此类警告。
CREATE EVENT
和ALTER EVENT
的情况下:
SLAVESIDE_DISABLED
,无论指定的状态如何(这不适用于DROP EVENT
)。
INFORMATION_SCHEMA.EVENTS
中的ORIGINATOR
列存储此信息。有关更多信息,请参见第 15.7.7.18 节,“SHOW EVENTS Statement”。
要确定在 MySQL 服务器上是否有任何在不同服务器(作为源服务器)上创建的计划事件,请以类似于此处所示的方式查询信息模式EVENTS
表:
SELECT EVENT_SCHEMA, EVENT_NAME
FROM INFORMATION_SCHEMA.EVENTS
WHERE STATUS = 'SLAVESIDE_DISABLED';
或者,您可以使用SHOW EVENTS
语句,如下所示:
SHOW EVENTS
WHERE STATUS = 'SLAVESIDE_DISABLED';
将具有此类事件的副本升级为源时,必须使用ALTER EVENT *
event_name* ENABLE
来启用每个事件,其中*event_name
*是事件的名称。
如果在创建此副本上的事件时涉及多个源,并且您希望识别仅在具有服务器 ID *source_id
*的给定源上创建的事件,请修改前面在EVENTS
表上的查询,包括ORIGINATOR
列,如下所示:
SELECT EVENT_SCHEMA, EVENT_NAME, ORIGINATOR
FROM INFORMATION_SCHEMA.EVENTS
WHERE STATUS = 'SLAVESIDE_DISABLED'
AND ORIGINATOR = '*source_id*'
您可以以类似的方式使用SHOW EVENTS
语句中的ORIGINATOR
:
SHOW EVENTS
WHERE STATUS = 'SLAVESIDE_DISABLED'
AND ORIGINATOR = '*source_id*'
在启用从源复制的事件之前,您应该在副本上禁用 MySQL 事件调度程序(使用类似于SET GLOBAL event_scheduler = OFF;
的语句),运行任何必要的ALTER EVENT
语句,重新启动服务器,然后在副本上重新启用事件调度程序(使用类似于SET GLOBAL event_scheduler = ON;
的语句)-
如果稍后将新源重新降级为副本,则必须手动禁用由ALTER EVENT
语句启用的所有事件。您可以通过在单独的表中存储先前显示的SELECT
语句中的事件名称,或使用ALTER EVENT
语句将事件重命名为具有replicated_
前缀的公共前缀来执行此操作。
如果您重命名事件,那么在将此服务器重新降级为副本时,您可以通过查询EVENTS
表来识别事件,如下所示:
SELECT CONCAT(EVENT_SCHEMA, '.', EVENT_NAME) AS 'Db.Event'
FROM INFORMATION_SCHEMA.EVENTS
WHERE INSTR(EVENT_NAME, 'replicated_') = 1;
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-json.html
在 MySQL 8.0 之前,对 JSON 列的更新始终被写入二进制日志作为完整文档。在 MySQL 8.0 中,可以记录 JSON 文档的部分更新(请参阅 JSON 值的部分更新),这更有效率。记录行为取决于所使用的格式,如下所述:
基于语句的复制。 JSON 部分更新始终被记录为部分更新。在使用基于语句的日志记录时,无法禁用此功能。
基于行的复制。 默认情况下,JSON 部分更新不会被记录为部分更新,而是被记录为完整文档。要启用部分更新的记录,请设置binlog_row_value_options=PARTIAL_JSON
。如果复制源设置了此变量,则来自该源的部分更新将由副本处理和应用,而不管副本自身对该变量的设置如何。
运行 MySQL 8.0.2 或更早版本的服务器无法识别用于 JSON 部分更新的日志事件。因此,当从运行 MySQL 8.0.3 或更高版本的服务器复制到此类服务器时,必须通过将此变量设置为''
(空字符串)在源端禁用binlog_row_value_options
。有关更多信息,请参阅此变量的描述。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-limit.html
在DELETE
、UPDATE
和INSERT ... SELECT
语句中,基于语句的复制LIMIT
子句是不安全的,因为受影响的行的顺序未定义。(只有当这些语句还包含ORDER BY
子句时,才能通过基于语句的复制正确复制这些语句。)当遇到这样的语句时:
STATEMENT
模式时,现在会发出警告,说明该语句对于基于语句的复制不安全。
在使用STATEMENT
模式时,即使 DML 语句包含LIMIT
并且还有ORDER BY
子句(因此变得确定性),也会发出警告。这是一个已知问题。(Bug #42851)
MIXED
模式时,该语句现在会自动使用基于行的模式进行复制。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-load-data.html
LOAD DATA
被认为在基于语句的日志记录中是不安全的(参见 Section 19.2.1.3, “二进制日志中安全和不安全语句的确定”)。当设置 binlog_format=MIXED
时,该语句以行格式记录。当设置 binlog_format=STATEMENT
时,请注意,与其他不安全语句不同,LOAD DATA
不会生成警告。
如果您使用 LOAD DATA
与 binlog_format=STATEMENT
,每个要应用更改的副本都会创建一个包含数据的临时文件。然后,副本使用 LOAD DATA
语句来应用更改。即使源上启用了二进制日志加密,此临时文件也不会被加密。如果需要加密,请改用基于行或混合的二进制日志格式,副本不会创建临时文件。
如果使用 PRIVILEGE_CHECKS_USER
帐户来帮助保护复制通道(参见 Section 19.3.3, “复制权限检查”),强烈建议您使用基于行的二进制日志记录(binlog_format=ROW
)记录 LOAD DATA
操作。如果为通道设置了 REQUIRE_ROW_FORMAT
,则需要基于行的二进制日志记录。使用此日志格式,执行事件不需要 FILE
权限,因此不要给 PRIVILEGE_CHECKS_USER
帐户此权限。如果需要从以语句格式记录的 LOAD DATA INFILE
操作的复制错误中恢复,并且复制的事件是可信的,则可以暂时授予 PRIVILEGE_CHECKS_USER
帐户 FILE
权限,在应用复制的事件后再删除该权限。
当mysqlbinlog读取以语句为基础格式记录的LOAD DATA
语句的日志事件时,会在临时目录中创建一个生成的本地文件。这些临时文件不会被mysqlbinlog或任何其他 MySQL 程序自动删除。如果您确实使用了以语句为基础的二进制日志记录的LOAD DATA
语句,那么在不再需要语句日志后,您应该自行删除这些临时文件。更多信息,请参阅 Section 6.6.9, “mysqlbinlog — 用于处理二进制日志文件的实用程序”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-max-allowed-packet.html
max_allowed_packet
设置了 MySQL 服务器和客户端之间的任何单个消息的大小上限,包括副本。如果在源端复制大型列值(例如可能在TEXT
或BLOB
列中找到的值),而max_allowed_packet
在源端设置过小,源端将会出现错误,并且副本会关闭复制 I/O(接收器)线程。如果副本端的max_allowed_packet
设置过小,也会导致副本停止 I/O 线程。
基于行的复制从源端向副本发送更新行的所有列和列值,包括实际上未被更新的列的值。这意味着,当您使用基于行的复制复制大型列值时,您必须确保将max_allowed_packet
设置得足够大,以容纳要复制的任何表中最大行的大小,即使您只复制更新,或者只插入相对较小的值。
在多线程复制(具有replica_parallel_workers > 0
或slave_parallel_workers > 0
)中,确保系统变量replica_pending_jobs_size_max
或slave_pending_jobs_size_max
的设置值等于或大于源端max_allowed_packet
系统变量的设置值。replica_pending_jobs_size_max
或slave_pending_jobs_size_max
的默认设置为 128M,是max_allowed_packet
系统变量的默认设置值 64M 的两倍。max_allowed_packet
限制了源端可以发送的数据包大小,但添加事件头可能会产生超过此大小的二进制日志事件。此外,在基于行的复制中,单个事件可能比max_allowed_packet
大小显著更大,因为max_allowed_packet
的值仅限制表的每列。
实际上,复制端接受的数据包上限由其replica_max_allowed_packet
或slave_max_allowed_packet
设置确定,默认设置为最大设置值 1GB,以防止由于大数据包而导致复制失败。然而,replica_pending_jobs_size_max
或slave_pending_jobs_size_max
的值控制了复制端可用于保存传入数据包的内存。指定的内存是在所有复制工作队列之间共享的。
replica_pending_jobs_size_max
或slave_pending_jobs_size_max
的值是一个软限制,如果一个异常大的事件(由一个或多个数据包组成)超过了这个大小,事务将被暂停,直到所有副本工作者的队列为空,然后再处理。所有后续事务都将被暂停,直到大事务完成。因此,虽然大于replica_pending_jobs_size_max
或slave_pending_jobs_size_max
的异常事件可以被处理,但清空所有副本工作者队列和等待排队后续事务的延迟可能导致副本延迟和副本工作者并发性降低。因此,replica_pending_jobs_size_max
或slave_pending_jobs_size_max
应设置为足够高,以容纳大多数预期事件大小。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-memory.html
当复制源服务器关闭并重新启动时,其MEMORY
表会变为空。为了将这种效果复制到副本中,源在启动后第一次使用给定的MEMORY
表时,会记录一个事件,通知副本该表必须通过向二进制日志写入DELETE
或(从 MySQL 8.0.22 开始)TRUNCATE TABLE
语句来清空该表。这个生成的事件在二进制日志中通过注释可识别,如果服务器上使用了 GTID,则会分配一个 GTID。该语句始终以语句格式记录,即使二进制日志格式设置为ROW
,并且即使服务器设置为read_only
或super_read_only
模式,也会写入。请注意,在源重新启动并首次使用表之间的间隔期间,副本仍然具有MEMORY
表中的过时数据。为了避免这种间隔,当直接查询副本可能返回陈旧数据时,您可以设置init_file
系统变量以命名一个包含在启动时在源上填充MEMORY
表的语句的文件。
当副本服务器关闭并重新启动时,其MEMORY
表会变为空。这会导致副本与源不同步,并可能导致其他故障或导致副本停止:
Can't find record in '*
memory_table*'
而失败。
INSERT INTO ... SELECT FROM *
memory_table*
之类的语句可能在源和副本上插入不同的行集。
副本还会将一个DELETE
或(从 MySQL 8.0.22 开始)TRUNCATE TABLE
语句写入其自己的二进制日志,传递给任何下游副本,导致它们清空自己的MEMORY
表。
重新启动正在复制MEMORY
表的副本的安全方法是首先在源上删除或清空所有MEMORY
表中的行,并等待这些更改复制到副本。然后才能安全地重新启动副本。
在某些情况下可能适用另一种重启方法。当binlog_format=ROW
时,如果在重新启动复制之前设置replica_exec_mode=IDEMPOTENT
(从 MySQL 8.0.26 开始)或slave_exec_mode=IDEMPOTENT
(在 MySQL 8.0.26 之前),则可以防止复制停止。这样可以使复制继续进行,但其MEMORY
表仍然与源端不同。如果应用逻辑允许MEMORY
表的内容安全丢失(例如,如果MEMORY
表用于缓存),那么这是可以接受的。replica_exec_mode=IDEMPOTENT
或slave_exec_mode=IDEMPOTENT
对所有表都适用,因此可能会隐藏非MEMORY
表中的其他复制错误。
(刚刚描述的方法在 NDB Cluster 中不适用,那里的replica_exec_mode
或slave_exec_mode
始终为IDEMPOTENT
,且无法更改。)
MEMORY
表的大小受max_heap_table_size
系统变量的值限制,该值不会被复制(参见 Section 19.5.1.39,“复制和变量”)。更改max_heap_table_size
对于使用ALTER TABLE ... ENGINE = MEMORY
或TRUNCATE TABLE
创建或更新的MEMORY
表会生效,或者对于所有在服务器重新启动后的MEMORY
表也会生效。如果你在源端增加了此变量的值而在复制端没有这样做,那么源端的表可能会比复制端的表更大,导致在源端成功插入但在复制端出现“表已满”错误。这是一个已知问题(Bug #48666)。在这种情况下,你必须在复制端和源端都设置max_heap_table_size
的全局值,然后重新启动复制。建议同时重新启动源端和复制端的 MySQL 服务器,以确保新值在它们各自上完全生效。
查看第 18.3 节,“MEMORY 存储引擎”,了解有关MEMORY
表的更多信息。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-mysqldb.html
对mysql
模式中的表进行的数据修改语句根据binlog_format
的值进行复制;如果该值为MIXED
,则使用基于行的格式复制这些语句。然而,通常间接更新此信息的语句,如GRANT
、REVOKE
以及操作触发器、存储过程和视图的语句,会使用基于语句的复制方式复制到副本。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-optimizer.html
如果一个语句被编写成非确定性的方式,即由查询优化器决定,那么源数据库和副本数据库上的数据可能会变得不同。(一般来说,这不是一个好的做法,即使在复制之外也是如此。)非确定性语句的例子包括使用LIMIT
而没有ORDER BY
子句的DELETE
或UPDATE
语句;详细讨论请参见 Section 19.5.1.18, “Replication and LIMIT”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-partitioning.html
只要分区表使用相同的分区方案并且结构相同,复制就支持分区表之间的复制,除非特别允许异常情况(参见 Section 19.5.1.9, “源表和副本表上定义不同的复制”)。
不同分区的表之间的复制通常不受支持。这是因为在这种情况下直接作用于分区的语句(比如ALTER TABLE ... DROP PARTITION
)可能会在源表和副本表上产生不同的结果。在源表分区但副本表未分区的情况下,任何在源表副本上操作分区的语句都会在副本表上失败。当副本表分区但源表未分区时,在源表上运行直接作用于分区的语句会导致错误。为避免停止复制或在源表和副本表之间创建不一致,始终确保源表和副本表的对应复制表以相同方式分区。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-repair-table.html
当在一个损坏或其他受损的表上使用REPAIR TABLE
语句时,可能会删除无法恢复的行。然而,此语句执行的任何表数据修改都不会被复制,这可能导致源和副本失去同步。因此,在源上的表损坏并使用REPAIR TABLE
修复之前,应该先停止复制(如果仍在运行),然后比较源和副本的表副本,并准备好在重新启动复制之前手动纠正任何差异。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-reserved-words.html
当您尝试从旧源复制到新副本并且在源上使用在新 MySQL 版本(在副本上运行)中被视为保留字的标识符时,可能会遇到问题。例如,在 MySQL 5.7 源上命名为 rank
的表列在复制到 MySQL 8.0 副本时可能会导致问题,因为 RANK
是 MySQL 8.0 中的保留字。
在这种情况下,即使从复制中排除使用保留字命名的数据库或表,或者具有使用保留字命名的列的表,复制也可能失败,并显示错误 1064“您的 SQL 语法有误…”。这是因为每个 SQL 事件在执行之前必须由副本解析,以便副本知道哪些数据库对象将受到影响。仅在事件解析后,副本才能应用由--replicate-do-db
、--replicate-do-table
、--replicate-ignore-db
和--replicate-ignore-table
定义的任何过滤规则。
要解决源数据库、表或列名称在副本中被视为保留字的问题,请执行以下操作之一:
ALTER TABLE
语句来更改任何数据库对象的名称,其中这些名称在副本中被视为保留字,并将使用旧名称的任何 SQL 语句更改为使用新名称。
有关 MySQL 版本的保留字列表,请参阅MySQL Server Version Reference中的 MySQL 8.0 中的关键字和保留字,有关标识符引用规则,请参阅第 11.2 节,“模式对象名称”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-row-searches.html
当使用基于行的复制格式的副本应用UPDATE
或DELETE
操作时,必须搜索相关表以查找匹配的行。执行此过程的算法首选使用表的一个索引进行搜索,如果没有合适的索引,则使用哈希表。
算法首先评估表定义中的可用索引,看是否有适合的索引可用,并且如果有多个可能性,哪个索引最适合该操作。算法忽略以下类型的索引:
如果在排除这些索引类型后没有合适的索引,则算法不使用索引进行搜索。如果有合适的索引,则从候选索引中选择一个索引,按以下优先顺序选择:
如果算法能够选择主键或唯一索引,其中索引中的每一列都具有NOT NULL
属性,则使用该索引来迭代UPDATE
或DELETE
操作中的行。对于行事件中的每一行,算法在索引中查找行以定位要更新的表记录。如果找不到匹配记录,则返回错误 ER_KEY_NOT_FOUND 并停止复制应用程序线程。
如果算法无法找到合适的索引,或者只能找到一个非唯一或包含空值的索引,那么将使用哈希表来帮助识别表记录。算法创建一个哈希表,其中包含UPDATE
或DELETE
操作中的行,键为行的完整前图像。然后,算法遍历目标表中的所有记录,如果找到索引则使用该索引,否则执行全表扫描。对于目标表中的每条记录,它确定该行是否存在于哈希表中。如果在哈希表中找到该行,则更新目标表中的记录,并从哈希表中删除该行。当检查完目标表中的所有记录后,算法验证哈希表是否为空。如果哈希表中仍有任何未匹配的行,则算法返回错误 ER_KEY_NOT_FOUND 并停止复制应用程序线程。
slave_rows_search_algorithms
系统变量以前用于控制如何搜索匹配的行。现在不推荐使用这个系统变量,因为默认设置,即使用索引扫描后跟随哈希扫描的方式,对性能最优且在所有场景下都能正常工作。
译文:
dev.mysql.com/doc/refman/8.0/en/replication-features-shutdowns.html
安全地关闭复制源服务器并稍后重新启动是安全的。当副本失去与源的连接时,副本会立即尝试重新连接,如果失败,则定期重试。默认情况下,每隔 60 秒重试一次。可以使用CHANGE REPLICATION SOURCE TO
语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO
语句(在 MySQL 8.0.23 之前)来更改此值。副本还能够处理网络连接中断。但是,副本只有在从源接收到replica_net_timeout
或slave_net_timeout
秒内没有数据后才会注意到网络中断。如果您的中断时间很短,您可能希望减少replica_net_timeout
或slave_net_timeout
的值。参见第 19.4.2 节,“处理副本意外停止”。
源端发生不干净的关闭(例如崩溃)可能导致源的二进制日志的最终位置小于副本最近读取的位置,因为源的二进制日志文件没有被刷新。这可能导致源重新启动时副本无法复制。在源服务器的my.cnf
文件中设置sync_binlog=1
有助于最小化此问题,因为它会导致源更频繁地刷新其二进制日志。对于使用带有事务的InnoDB
的复制设置,为了获得最大可能的耐久性和一致性,还应该设置innodb_flush_log_at_trx_commit=1
。使用此设置,InnoDB
重做日志缓冲区的内容在每个事务提交时写入日志文件,并且日志文件被刷新到磁盘。请注意,即使使用此设置,事务的耐久性仍然不能得到保证,因为操作系统或磁盘硬件可能告诉mysqld刷新到磁盘的操作已经完成,尽管实际上并没有。
干净地关闭一个副本是安全的,因为它会记录下关闭时的位置。但是,请注意副本没有打开临时表;参见 Section 19.5.1.31,“复制和临时表”。不干净的关闭可能会产生问题,特别是如果在问题发生之前磁盘缓存没有刷新到磁盘上:
relay-log.info
。如果在这两个操作之间发生意外退出,中继日志处理会继续进行,超过信息文件所指示的位置,并且在重新启动后,副本会重新执行中继日志中最后一个事务之后的事件。
relay-log.info
但是在写入刷新到磁盘之前服务器主机崩溃,就会出现类似的问题。为了最大程度减少这种情况发生的机会,在副本的my.cnf
文件中设置sync_relay_log_info=1
。将sync_relay_log_info
设置为 0 会导致不强制写入磁盘,服务器依赖操作系统不时地刷新文件。
对于这些问题类型,系统的容错性会大大增加,如果你有一个良好的不间断电源供应。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-errors.html
如果一条语句在源端和副本端产生相同的错误(相同的错误代码),错误会被记录,但复制会继续。
如果一条语句在源端和副本端产生不同的错误,复制 SQL 线程将终止,并且副本会在其错误日志中写入一条消息,等待数据库管理员决定如何处理该错误。这包括一种情况,即一条语句在源端或副本端产生错误,但不是同时在两端都出错。要解决这个问题,需要手动连接到副本并确定问题的原因。SHOW REPLICA STATUS
(或在 MySQL 8.0.22 之前,SHOW SLAVE STATUS
(或在 MySQL 8.0.22 之前,START SLAVE
选项屏蔽(忽略)一些或所有错误。
对于非事务性存储引擎,如MyISAM
,可能会出现仅部分更新表并返回错误代码的语句。例如,在多行插入中有一行违反键约束,或者在更新了部分行后长时间的更新语句被终止。如果这种情况发生在源端,副本期望执行该语句会产生相同的错误代码。如果没有产生相同的错误代码,复制 SQL 线程将如前所述停止。
如果在源表和副本上使用不同存储引擎的表之间进行复制,请记住,同一语句在针对表的一个版本运行时可能会产生不同的错误,但在另一个版本上不会,或者可能会导致一个版本的表出现错误,而另一个版本不会。例如,由于MyISAM
忽略外键约束,访问源上的InnoDB
表的INSERT
或UPDATE
语句可能会导致外键违反,但在副本上的相同语句执行在MyISAM
版本的相同表上不会产生此类错误,导致复制停止。
从 MySQL 8.0.31 开始,复制过滤规则首先应用,然后再进行任何权限或行格式检查,这样可以过滤掉任何未通过验证的事务;对于已被过滤的事务不执行任何检查,因此不会引发错误。这意味着副本只能接受给定用户已被授予访问权限的数据库部分(只要该数据库部分的更新使用基于行的复制格式)。在进行升级或迁移到一个使用入站复制用户无法访问管理表的系统或应用程序时,这可能会有所帮助。另请参阅第 19.2.5 节,“服务器如何评估复制过滤规则”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-sql-mode.html
在源数据库和副本数据库上使用不同的服务器 SQL 模式设置可能导致相同的INSERT
语句在源数据库和副本数据库上处理方式不同,导致源数据库和副本数据库分歧。为了获得最佳结果,您应该始终在源数据库和副本数据库上使用相同的服务器 SQL 模式。无论您使用基于语句还是基于行的复制,这些建议都适用。
如果您正在复制分区表,并且在源数据库和副本数据库上使用不同的 SQL 模式,可能会导致问题。至少,这可能导致数据在源数据库和副本数据库中的分区分布不同。这也可能导致在源数据库上成功插入分区表的数据,在副本数据库上失败。
更多信息,请参见 Section 7.1.11, “Server SQL Modes”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-temptables.html
在 MySQL 8.0 中,当binlog_format
设置为ROW
或MIXED
时,仅使用临时表的语句不会在源上记录,因此临时表不会被复制。涉及临时表和非临时表混合的语句仅在源上为非临时表的操作记录,临时表的操作不会记录。这意味着在副本发生意外关闭时,副本上永远不会有临时表丢失。有关基于行的复制和临时表的更多信息,请参阅基于行的临时表记录。
当binlog_format
设置为STATEMENT
时,涉及临时表的语句在源上记录并在副本上复制,前提是涉及临时表的语句可以安全地使用基于语句的格式记录。在这种情况下,在副本上丢失复制的临时表可能是一个问题。在基于语句的复制模式中,当服务器上使用 GTIDs 时(即,当enforce_gtid_consistency
系统变量设置为ON
时),不能在事务、过程、函数或触发器中使用CREATE TEMPORARY TABLE
和DROP TEMPORARY TABLE
语句。当使用 GTIDs 时,可以在这些上下文之外使用它们,前提是设置了autocommit=1
。
由于基于行或混合复制模式与基于语句的复制模式在临时表行为上的差异,如果更改适用于包含任何打开临时表的上下文(全局或会话),则不能在运行时切换复制格式。有关更多详细信息,请参阅binlog_format
选项的描述。
在使用临时表时安全地关闭复制。 在基于语句的复制模式下,临时表会被复制,除非您停止复制服务器(而不仅仅是复制线程),并且您已经复制了在副本上尚未执行的更新中使用的临时表。如果停止复制服务器,则在重新启动副本时,这些更新所需的临时表将不再可用。为了避免这个问题,请不要在副本有打开的临时表时关闭副本。而是使用以下过程:
STOP REPLICA SQL_THREAD
语句。
SHOW STATUS
来检查Replica_open_temp_tables
或Slave_open_temp_tables
状态变量的值。
START REPLICA SQL_THREAD
重新启动复制 SQL 线程,稍后重复该过程。
临时表和复制选项。 默认情况下,使用基于语句的复制时,所有临时表都会被复制;无论是否存在任何匹配的--replicate-do-db
,--replicate-do-table
,或--replicate-wild-do-table
选项。但是,对于临时表,会遵守--replicate-ignore-table
和--replicate-wild-ignore-table
选项。唯一的例外是,为了在会话结束时正确删除临时表,复制总是会复制DROP TEMPORARY TABLE IF EXISTS
语句,而不管通常适用于指定表的任何排除规则。
在使用基于语句的复制时,建议指定一个专用前缀用于命名不希望被复制的临时表,然后使用--replicate-wild-ignore-table
选项来匹配该前缀。例如,您可以给所有这样的表命名以norep
开头(如norepmytable
,norepyourtable
等),然后使用--replicate-wild-ignore-table=norep%
来阻止它们被复制。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-timeout.html
系统变量replica_transaction_retries
的全局值(从 MySQL 8.0.26 开始)或slave_transaction_retries
(在 MySQL 8.0.26 之前)设置了单线程或多线程复制品上应用程序线程在停止之前自动重试失败事务的最大次数。当 SQL 线程由于InnoDB
死锁而无法执行事务,或者事务的执行时间超过InnoDB``innodb_lock_wait_timeout
值时,事务会自动重试。如果事务有一个阻止其成功的非临时错误,则不会重试。
默认设置为replica_transaction_retries
或slave_transaction_retries
为 10,意味着在出现明显临时错误的失败事务会在停止应用程序线程之前重试 10 次。将该变量设置为 0 会禁用事务的自动重试。在多线程复制中,指定的事务重试次数可以在所有通道的所有应用程序线程上进行。性能模式表replication_applier_status
显示了每个复制通道上发生的事务重试总数,在COUNT_TRANSACTIONS_RETRIES
列中。
重试事务的过程可能导致复制品或组复制组成员出现滞后,可以将其配置为单线程或多线程复制品。性能模式表replication_applier_status_by_worker
显示了单线程或多线程复制品上应用程序线程重试事务的详细信息。这些数据包括时间戳,显示应用程序线程从开始到结束应用最后一个事务所花费的时间(以及当前正在进行的事务何时开始),以及这是在原始来源和直接来源上提交后多长时间。数据还显示了最后一个事务和当前正在进行的事务的重试次数,并使您能够识别导致事务重试的瞬时错误。您可以使用此信息查看事务重试是否导致复制滞后,并调查导致重试的失败的根本原因。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-timezone.html
默认情况下,源和副本服务器假定它们处于相同的时区。如果在不同时区的服务器之间进行复制,则必须在源和副本上设置时区。否则,依赖源本地时间的语句将无法正确复制,例如使用NOW()
或FROM_UNIXTIME()
函数的语句。
请验证源和副本的系统时区(system_time_zone
)、服务器当前时区(time_zone
的全局值)和会话时区(time_zone
的会话值)的设置组合是否产生正确的结果。特别是,如果time_zone
系统变量设置为值SYSTEM
,表示服务器时区与系统时区相同,则这可能导致源和副本应用不同的时区。例如,源可能在二进制日志中写入以下语句:
SET @@session.time_zone='SYSTEM';
如果此源及其副本对其系统时区设置不同,则即使副本的全局time_zone
值已设置为与源相匹配,此语句也可能在副本上产生意外结果。有关 MySQL Server 的时区设置说明以及如何更改它们,请参阅 第 7.1.15 节,“MySQL Server 时区支持”。
请参阅 第 19.5.1.14 节,“复制和系统函数”。
译文:
dev.mysql.com/doc/refman/8.0/en/replication-features-transaction-inconsistencies.html
根据您的复制配置,从中继日志中执行的事务序列可能存在不一致性。本节解释了如何避免不一致性并解决其引起的任何问题。
可能存在以下类型的不一致性:
replica_preserve_commit_order=ON
(从 MySQL 8.0.26 开始)或slave_preserve_commit_order=ON
(在 MySQL 8.0.26 之前)。从 MySQL 8.0.27 开始,默认情况下设置此选项,因为从该版本开始,默认情况下所有副本都是多线程的。
直到 MySQL 8.0.18,保留提交顺序需要启用二进制日志(log_bin
)和副本更新日志(log_replica_updates
或log_slave_updates
),这是从 MySQL 8.0 开始的默认设置。从 MySQL 8.0.19 开始,在副本上设置replica_preserve_commit_order=ON
或slave_preserve_commit_order=ON
不需要启用二进制日志和副本更新日志,并且如果需要,可以禁用。
在所有版本中,设置replica_preserve_commit_order=ON
或slave_preserve_commit_order=ON
需要设置replica_parallel_type
(从 MySQL 8.0.26 开始)或slave_parallel_type
(在 MySQL 8.0.26 之前)为LOGICAL_CLOCK
。从 MySQL 8.0.27 开始(但不适用于早期版本),这是默认设置。
在某些特定情况下,如replica_preserve_commit_order
和slave_preserve_commit_order
的描述中列出的情况,设置replica_preserve_commit_order=ON
或slave_preserve_commit_order=ON
无法在复制品上保留提交顺序,因此在这些情况下,序列中仍可能出现间隙。
设置replica_preserve_commit_order=ON
或slave_preserve_commit_order=ON
不会阻止源二进制日志位置滞后。
Exec_master_log_pos
之后应用了事务。也就是说,直到点N
之前的所有事务都已应用,但在点N
之后没有应用任何事务,但Exec_master_log_pos
的值小于N
。在这种情况下,Exec_master_log_pos
是已应用事务的“低水位标记”,落后于最近应用事务的位置。这只会发生在多线程复制中。启用replica_preserve_commit_order
或slave_preserve_commit_order
不会阻止源二进制日志位置滞后。
以下情景与部分应用事务、间隙和源二进制日志位置滞后有关:
KILL
(在使用单线程复制时为 SQL 线程,在使用多线程复制时为协调器线程)。这会中止正在进行的事务,并可能留下间隙和部分应用事务。
STOP REPLICA
在使用多线程复制时使用。发出 STOP REPLICA
后,复制品等待任何间隙被填充,然后更新 Exec_master_log_pos
。这确保它永远不会留下间隙或源二进制日志位置滞后,除非上述任何情况适用,换句话说,在 STOP REPLICA
完成之前,要么发生错误,要么另一个线程发出 KILL
,要么服务器重新启动。在这些情况下,STOP REPLICA
返回成功。
STOP REPLICA
最多等待 60 秒以接收事务。超时后,协调器放弃并中止事务。如果事务是混合的,则可能会留下未完成的部分。
STOP REPLICA
立即回滚并停止。如果正在进行的事务是混合的,STOP REPLICA
最多等待 60 秒以完成事务。超时后,它会中止事务,因此可能会留下未完成的部分。
系统变量 rpl_stop_replica_timeout
(从 MySQL 8.0.26 开始)或 rpl_stop_slave_timeout
(在 MySQL 8.0.26 之前)的全局设置与停止复制线程的过程无关。它只是使发出 STOP REPLICA
的客户端返回给客户端,但���制线程继续尝试停止。
如果复制通道存在间隙,会产生以下后果:
SHOW REPLICA STATUS
中的 Exec_master_log_pos
字段仅是“低水位标记”。换句话说,出现在该位置之前的事务已经提交,但在该位置之后的事务可能已经提交,也可能没有。
CHANGE REPLICATION SOURCE TO
和 CHANGE MASTER TO
语句将因错误而失败,除非应用程序线程正在运行且语句仅设置接收器选项。
--relay-log-recovery
启动mysqld,则不会为该通道执行恢复,并会打印警告。
--dump-replica
或--dump-slave
来使用mysqldump,它不会记录间隙的存在;因此,它会打印CHANGE REPLICATION SOURCE TO
| CHANGE MASTER TO
,并将RELAY_LOG_POS
设置为Exec_master_log_pos
中的“低水位”位置。
在另一台服务器上应用转储并启动复制线程后,出现在该位置之后的事务将再次被复制。请注意,如果启用了 GTID(但在这种情况下不建议使用--dump-replica
或--dump-slave
),这是无害的。
如果复制通道存在源二进制日志位置滞后但没有间隙,则适用于上述情况 2 至 5,但不适用于情况 1。
源二进制日志位置信息以二进制格式持久化存储在内部表mysql.slave_worker_info
中。START REPLICA [SQL_THREAD]始终会查阅此信息,以便仅应用正确的事务。即使在START REPLICA
之前将replica_parallel_workers
或slave_parallel_workers
更改为 0,甚至在使用UNTIL
子句的情况下使用START REPLICA
,这仍然有效。START REPLICA UNTIL SQL_AFTER_MTS_GAPS
仅应用所需数量的事务以填补间隙。如果在消耗所有间隙之前告诉START REPLICA
停止的UNTIL
子句,则会留下剩余的间隙。
警告
RESET REPLICA
会删除中继日志并重置复制位置。因此,在具有间隙的多线程复制中发出RESET REPLICA
意味着复制丢失了有关间隙的任何信息,而没有纠正间隙。在这种情况下,如果使用基于二进制日志位置的复制,则恢复过程将失败。
当使用基于 GTID 的复制时(GTID_MODE=ON
),并且使用CHANGE REPLICATION SOURCE TO
语句为复制通道设置了SOURCE_AUTO_POSITION
时,旧的中继日志在恢复过程中不再需要。相反,复制品可以使用 GTID 自动定位来计算与源相比缺少的事务。从 MySQL 8.0.26 开始,在使用基于 GTID 的复制时,用于在多线程复制品上解决间隙的基于二进制日志位置的过程将完全跳过。当跳过该过程时,START REPLICA UNTIL SQL_AFTER_MTS_GAPS
语句的行为会有所不同,并且不会尝试检查事务序列中的间隙。您还可以发出CHANGE REPLICATION SOURCE TO
语句,在非 GTID 复制品上不允许存在间隙的情况下。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-transactions.html
在同一事务中混合事务和非事务语句。 一般来说,在复制环境中,应避免更新既有事务表又有非事务表的事务。还应避免使用任何访问既有事务(或临时)表又访问非事务表并写入其中的语句。
服务器使用以下规则进行二进制日志记录:
binlog_direct_non_transactional_updates
系统变量的影响。当此变量为OFF
(默认值)时,记录如上所述。当此变量为ON
时,对于事务中发生的任何非事务语句,立即记录(不仅仅是初始的非事务语句)。其他语句被保留在事务缓存中,并在事务提交时记录。binlog_direct_non_transactional_updates
对于行格式或混合格式的二进制日志记录没有影响。
事务、非事务和混合语句。 为了应用这些规则,服务器认为一个语句是非事务的,如果它只改变非事务表,而认为是事务的,如果它只改变事务表。一个引用了非事务表和事务表并更新任何涉及的表的语句被视为“混合”语句。混合语句,像事务语句一样,在事务提交时被缓存和记录。
更新事务表的混合语句,如果该语句还执行以下任一操作,则被视为不安全:
在事务中更新事务表后的混合语句,如果执行以下任一操作,则被视为不安全:
binlog_direct_non_transactional_updates
为关闭状态
更多信息,请参见 Section 19.2.1.3, “Determination of Safe and Unsafe Statements in Binary Logging”.
注意
混合语句与混合二进制日志格式无关。
在事务混合更新事务和非事务表的情况下,二进制日志中语句的顺序是正确的,即使在ROLLBACK
的情况下,所有需要的语句也会被写入二进制日志。然而,当第二个连接在第一个连接事务完成之前更新非事务表时,语句可能会因为第二个连接的更新立即写入,而不考虑第一个连接正在执行的事务状态,导致日志顺序混乱。
在源和副本上使用不同的存储引擎。 可以在源上使用非事务表复制事务表到副本上。例如,可以将InnoDB
源表复制为MyISAM
副本表。但是,如果这样做,在BEGIN
… COMMIT
块中停止副本会出现问题,因为副本会在BEGIN
块的开头重新启动。
将MyISAM
表上的事务复制到副本上的事务表,例如使用InnoDB
存储引擎的表,也是安全的。在这种情况下,在源上发出的AUTOCOMMIT=1
语句会被复制,从而在副本上强制执行AUTOCOMMIT
模式。
当副本的存储引擎类型为非事务时,应避免在源上混合更新事务和非事务表的事务,因为它们可能导致源事务表和副本非事务表之间的数据不一致。也就是说,这样的事务可能导致源存储引擎特定行为,可能导致复制失去同步。MySQL 不会对此发出警告,因此在将源上的事务表复制到副本上的非事务表时,应格外小心。
更改事务中的二进制日志格式。 只要事务正在进行中,binlog_format
和binlog_checksum
系统变量是只读的。
每个事务(包括autocommit
事务)都被记录在二进制日志中,就好像它以BEGIN
语句开始,以COMMIT
或ROLLBACK
语句结束。即使是影响使用非事务性存储引擎(如MyISAM
)的表的语句也是如此。
注意
对于仅适用于 XA 事务的限制,请参见第 15.3.8.3 节,“XA 事务的限制”。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-triggers.html
在基于语句的复制中,源上执行的触发器也会在副本上执行。在基于行的复制中,源上执行的触发器不会在副本上执行。相反,源上由触发器执行导致的行更改会被复制并应用到副本上。
这种行为是有意设计的。如果在基于行的复制下,副本服务器应用触发器以及由它们引起的行更改,那么实际上更改会在副本上应用两次,导致源和副本上的数据不同。
如果希望触发器在源和副本上都执行,可能是因为源和副本上有不同的触发器,则必须使用基于语句的复制。然而,为了启用副本端触发器,不必完全使用基于语句的复制。只需在希望产生此效果的语句上切换到基于语句的复制即可,其余时间继续使用基于行的复制。
调用导致对AUTO_INCREMENT
列进行更新的触发器(或函数)的语句在使用基于语句的复制时无法正确复制。MySQL 8.0 将这类语句标记为不安全。(Bug #45677)
触发器可以针对不同的触发事件组合(INSERT
、UPDATE
、DELETE
)和动作时间(BEFORE
、AFTER
)拥有触发器,并且允许多个触发器。
为简洁起见,“多个触发器”在这里是“具有相同触发事件和动作时间的多个触发器”的简称。
升级。 早于 MySQL 5.7 版本不支持多个触发器。如果在使用早于 MySQL 5.7 的版本的复制拓扑中升级服务器,请先升级副本,然后再升级源。如果升级后的复制源服务器仍有使用不支持多个触发器的 MySQL 版本的旧副本,那么如果在源上为已经具有相同触发事件和动作时间的触发器的表创建触发器,则在这些副本上会出现错误。
降级。 如果将支持多个触发器的服务器降级到不支持多个触发器的旧版本,降级会产生以下影响:
.TRG
文件中。然而,如果存在具有相同触发事件和动作时间的多个触发器,当触发事件发生时,服务器只会执行其中一个。有关.TRG
文件的信息,请参阅 MySQL 服务器 Doxygen 文档中的表触发器存储部分,网址为dev.mysql.com/doc/index-other.html
。
.TRG
文件。重写后的文件仅保留每个触发器事件和动作时间组合中的一个触发器;其他触发器将丢失。
为避免这些问题,在降级之前修改您的触发器。对于每个表,如果每个触发器事件和动作时间组合中有多个触发器,请将每组触发器转换为单个触发器,方法如下:
NEW
和OLD
访问的值可以通过参数传递给存储过程。如果触发器需要代码中的单个结果值,可以将代码放入存储函数中,并让函数返回该值。如果触发器需要代码中的多个结果值,可以将代码放入存储过程中,并使用OUT
参数返回这些值。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-truncate.html
TRUNCATE TABLE
通常被视为 DML 语句,因此在二进制日志记录模式为ROW
或MIXED
时,预计会使用基于行的格式进行记录和复制。然而,在以STATEMENT
或MIXED
模式记录或复制时,当事务隔离级别为READ COMMITTED
或READ UNCOMMITTED
时,这会导致在使用事务性存储引擎如InnoDB
的表时出现问题,这种情况排除了基于语句的记录。
TRUNCATE TABLE
在记录和复制方面被视为 DDL 而不是 DML,以便可以将其记录和复制为语句。然而,对于副本上的InnoDB
和其他事务性表的影响仍遵循第 15.1.37 节“TRUNCATE TABLE Statement”中描述的规则。 (Bug #36763)
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-user-names.html
MySQL 8.0 中用户名称的最大长度为 32 个字符。 当副本运行的 MySQL 版本早于 5.7 时,长度超过 16 个字符的用户名称的复制将失败,因为这些版本仅支持较短的用户名称。 这仅在从更新的源复制到较旧的副本时发生,这不是推荐的配置。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-variables.html
使用STATEMENT
模式时,系统变量在使用会话范围时不会被正确复制,除了以下变量:
auto_increment_increment
auto_increment_offset
character_set_client
character_set_connection
character_set_database
character_set_server
collation_connection
collation_database
collation_server
foreign_key_checks
identity
last_insert_id
lc_time_names
pseudo_thread_id
sql_auto_is_null
time_zone
timestamp
unique_checks
当使用MIXED
模式时,前述列表中的变量在会话范围内使用时会导致从基于语句的日志记录切换到基于行的日志记录。请参阅 Section 7.4.4.3, “Mixed Binary Logging Format”。
sql_mode
也会被复制,除了NO_DIR_IN_CREATE
模式;复制品始终保留自己的NO_DIR_IN_CREATE
值,而不管源上对其进行了何种更改。这对所有复制格式都适用。
然而,当mysqlbinlog解析SET @@sql_mode = *
mode*
语句时,包括NO_DIR_IN_CREATE
在内的完整*mode
*值将传递给接收服务器。因此,在使用STATEMENT
模式时,此类语句的复制可能不安全。
default_storage_engine
系统变量不会被复制,无论日志记录模式如何;这旨在促进不同存储引擎之间的复制。
read_only
系统变量不会被复制。此外,启用此变量在不同的 MySQL 版本中对临时表、表锁定和 SET PASSWORD
语句产生不同的影响。
max_heap_table_size
系统变量不会被复制。在源上增加此变量的值而在副本上未这样做最终可能导致在副本上执行 MEMORY
表的 INSERT
语句时出现 Table is full 错误,因为源上的表被允许比副本上的表更大。有关更多信息,请参见 Section 19.5.1.21, “Replication and MEMORY Tables”。
在基于语句的复制中,当在更新表的语句中使用会话变量时,会话变量不会被正确复制。例如,以下语句序列在源和副本上不会插入相同的数据:
SET max_join_size=1000;
INSERT INTO mytable VALUES(@@max_join_size);
这不适用于常见的顺序:
SET time_zone=...;
INSERT INTO mytable VALUES(CONVERT_TZ(..., ..., @@time_zone));
当使用基于行的复制时,会话变量的复制不是问题,此时会话变量始终安全地被复制。参见 Section 19.2.1, “Replication Formats”。
以下会话变量被写入二进制日志,并在解析二进制日志时由副本进行尊重,无论日志格式如何:
sql_mode
foreign_key_checks
unique_checks
character_set_client
collation_connection
collation_database
collation_server
sql_auto_is_null
重要
尽管与字符集和校对有关的会话变量被写入二进制日志,但不支持不同字符集之间的复制。
为了减少可能的混淆,我们建议您始终在源和副本上使用相同的设置来配置 lower_case_table_names
系统变量,特别是当您在区分大小写的文件系统上运行 MySQL 时。lower_case_table_names
设置只能在初始化服务器时配置。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-features-views.html
视图始终会被复制到副本中。视图是根据其自身名称进行过滤的,而不是根据它们所引用的表进行过滤。这意味着即使视图包含通常会被replication-ignore-table
规则过滤掉的表,视图也可以被复制到副本中。因此,应该注意确保视图不会复制通常出于安全原因而被过滤的表数据。
使用基于语句的日志记录支持从表复制到同名视图,但在使用基于行的日志记录时不支持。在启用基于行的日志记录时尝试这样做会导致错误。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-compatibility.html
MySQL 支持从一个发布系列复制到下一个更高的发布系列。例如,您可以从运行 MySQL 5.6 的源复制到运行 MySQL 5.7 的副本,从运行 MySQL 5.7 的源复制到运行 MySQL 8.0 的副本,依此类推。但是,如果源使用在副本上使用的 MySQL 版本中不再支持的语句或行为,可能会在从旧源复制到新副本时遇到困难。例如,MySQL 8.0 不再支持超过 64 个字符的外键名称。
在涉及多个源的复制设置中,不支持使用两个以上的 MySQL 服务器版本,无论源或副本 MySQL 服务器的数量如何。这个限制不仅适用于发布系列,还适用于同一发布系列中的版本号。例如,如果您正在使用链式或循环复制设置,您不能同时使用 MySQL 8.0.22、MySQL 8.0.24 和 MySQL 8.0.28,尽管您可以同时使用这些版本中的任意两个。
重要提示
强烈建议在给定的 MySQL 发布系列中使用最新版本,因为复制(和其他)功能不断得到改进。还建议在 MySQL 发布系列的早期版本的源和副本可用时升级到 GA(生产)版本。
从 MySQL 8.0.14 开始,二进制日志中为每个事务记录了原始提交事务的服务器版本(original_server_version
),以及在复制拓扑中当前服务器的直接源服务器的服务器版本(immediate_server_version
)。
从更新的源到旧的副本的复制可能是可能的,但通常不受支持。这是由于许多因素:
在 MySQL 8.0.26 中,对复制仪器名称进行了不兼容的更改,包括线程阶段的名称,其中包含术语“master”,被更改为“source”,“slave”,被更改为“replica”,以及“mts”(代表“多线程从属”),被更改为“mta”(代表“多线程应用程序”)。使用这些仪器名称的监控工具可能会受到影响。如果不兼容的更改对您产生影响,请将terminology_use_previous
系统变量设置为BEFORE_8_0_26
,以使 MySQL Server 使用前面列表中指定对象的旧版本名称。这样可以使依赖于旧名称的监控工具继续工作,直到它们可以更新为使用新名称。
有关潜在复制问题的更多信息,请参见第 19.5.1 节,“复制功能和问题”。
当您升级参与复制拓扑的服务器时,您需要考虑每个服务器在拓扑中的角色,并注意与复制相关的问题。有关升级 MySQL 服务器实例的一般信息和说明,请参阅第三章,升级 MySQL。
如第 19.5.2 节,“MySQL 版本之间的复制兼容性”中所解释的,MySQL 支持从运行一个发布系列的源到运行下一个更高发布系列的副本的复制,但不支持从运行较新发布的源到运行较早发布的副本的复制。较早发布的副本可能没有处理源在较新发布中可以处理的事务所需的能力。因此,在升级源服务器到目标发布之前,您必须将复制拓扑中的所有副本升级到目标 MySQL 服务器版本。这样,您永远不会出现仍在较早发布的副本尝试处理来自较新发布源的事务的情况。
在存在多个源(多源复制)的复制拓扑中,不支持使用两个以上的 MySQL 服务器版本,无论源或副本 MySQL 服务器的数量如何。此限制不仅适用于发布系列,还适用于同一发布系列中的版本号。例如,在这样的设置中,您不能同时使用 MySQL 8.0.22、MySQL 8.0.24 和 MySQL 8.0.28,尽管您可以同时使用这些版本中的任意两个。
如果您需要降级复制拓扑中的服务器,则必须在降级副本之前降级源。在副本上,您必须确保二进制日志和中继日志已完全处理,并在继续降级之前将其删除。
虽然这个升级顺序是正确的,但在从一个尚未升级的早期版本源复制到一个已经升级的后续版本副本时,仍然可能遇到复制困难。如果源使用了在副本上安装的后续版本中不再支持的语句或依赖行为,这种情况可能发生。您可以使用 MySQL Shell 的升级检查工具util.checkForServerUpgrade()
来检查 MySQL 5.7 服务器实例或 MySQL 8.0 服务器实例是否可以升级到 GA MySQL 8.0 版本。该工具识别需要修复的任何内容,以确保在升级后不会出现问题,包括在后续版本中不再可用的功能和行为。有关升级检查工具的信息,请参见升级检查工具。
如果您正在将现有的 MySQL 版本不支持全局事务标识符(GTID)的复制设置升级到支持 GTID 的版本,则只有在确保设置符合基于 GTID 的复制的所有要求时,才在源和副本上启用 GTID。有关将基于二进制日志文件位置的复制设置转换为使用基于 GTID 的复制的信息,请参见 Section 19.1.3.4, “Setting Up Replication Using GTIDs”。
在严格 SQL 模式下(STRICT_TRANS_TABLES
或STRICT_ALL_TABLES
)影响操作的更改可能导致升级后副本的复制失败。如果使用基于语句的日志记录(binlog_format=STATEMENT
),如果副本在源之前升级,源执行的语句在那里成功,但在副本上可能失败,从而导致复制停止。为了解决这个问题,停止源上的所有新语句,并等待副本赶上,然后升级副本。或者,如果无法停止新语句,暂时在源上切换到基于行的日志记录(binlog_format=ROW
),并等待所有副本处理到达此更改点之前产生的所有二进制日志,然后升级副本。
默认字符集从latin1
更改为utf8mb4
在 MySQL 8.0 中。在复制设置中,当从 MySQL 5.7 升级到 8.0 时,建议在升级之前将默认字符集更改回 MySQL 5.7 中使用的字符集。升级完成后,可以将默认字符集更改为utf8mb4
。假设之前使用了默认设置,保留它们的一种方法是在my.cnf
文件中使用以下行启动服务器:
[mysqld]
character_set_server=latin1
collation_server=latin1_swedish_ci
要升级复制拓扑,请按照第三章“升级 MySQL”中的说明为每个单独的 MySQL 服务器实例执行此整体过程:
gtid_mode=ON
),不要通过mysql_upgrade启用二进制日志记录(因此不要使用--write-binlog
选项)。然后关闭并重新启动服务器。
START REPLICA
或START SLAVE
语句重新启动复制。
START REPLICA
或START SLAVE
语句。如果您对基于行的日志记录或默认字符集进行了临时更改,现在可以恢复更改。
在从一个 MySQL 系列移动到下一个系列时,某些升级可能需要您删除并重新创建数据库对象。例如,排序规则更改可能需要重建表索引。如果需要这样的操作,则在第 3.5 节“MySQL 8.0 中的更改”中有详细说明。最安全的做法是在副本和源上分别执行这些操作,并禁用从源到副本的这些操作的复制。为此,请使用以下过程:
--skip-slave-start
选项或从 MySQL 8.0.24 开始,使用skip_slave_start
系统变量重新启动它们,以便它们不连接到源。执行任何需要重新创建数据库对象的表修复或重建操作,例如使用REPAIR TABLE
或ALTER TABLE
,或者转储和重新加载表或触发器。
SET sql_log_bin = OFF
语句。或者,停止源并使用--skip-log-bin
选项重新启动它。如果重新启动源,则可能还希望禁止客户端连接。例如,如果所有客户端都使用 TCP/IP 连接,请在重新启动源时启用skip_networking
系统变量。
sql_log_bin
设置为OFF
,则执行SET sql_log_bin = ON
语句。如果重新启动源以禁用二进制日志,请在不使用--skip-log-bin
的情况下重新启动,并且不启用skip_networking
系统变量,以便客户端和复制实例可以连接。
--skip-slave-start
选项或skip_slave_start
系统变量。
原文:
dev.mysql.com/doc/refman/8.0/en/replication-problems.html
如果您已按照说明操作,但复制设置无法正常工作,首先要做的是检查错误日志中的消息。许多用户在遇到问题后没有及时这样做而浪费了时间。
如果您无法从错误日志中确定问题所在,请尝试以下技术:
验证源是否启用了二进制日志记录,通过发出SHOW MASTER STATUS
语句进行验证。二进制日志记录默认启用。如果启用了二进制日志记录,则Position
不为零。如果未启用二进制日志记录,请验证您是否未使用任何禁用二进制日志记录的设置运行源,例如--skip-log-bin
选项。
验证server_id
系统变量在源和副本上启动时是否已设置,并且 ID 值在每台服务器上是唯一的。
验证副本是否正在运行。使用SHOW REPLICA STATUS
检查Replica_IO_Running
和Replica_SQL_Running
的值是否都为Yes
。如果不是,请验证启动副本服务器时使用的选项。例如,--skip-slave-start
命令行选项,或者从 MySQL 8.0.24 开始,skip_slave_start
系统变量,阻止复制线程启动,直到您发出START REPLICA
语句。
如果副本正在运行,请检查它是否已经与源建立连接。使用SHOW PROCESSLIST
,找到 I/O(接收器)和 SQL(应用程序)线程,并检查它们的State
列以查看显示的内容。参见 Section 19.2.3, “Replication Threads”。如果接收器线程状态显示Connecting to master
,请检查以下内容:
3306
)。对于主机名,请确保名称解析为正确的 IP 地址。
skip_networking
系统变量以禁用网络。如果是,请注释该设置或将其删除。
ping
或traceroute
/tracert
到达主机来检查是否可以访问源。
如果副本以前正在运行但已停止,则原因通常是在源上成功运行的某个语句在副本上失败。如果您已经正确地对源进行了快照,并且从未在复制线程之外修改副本上的数据,则不应该发生这种情况。如果副本意外停止,则是一个错误,或者您遇到了 Section 19.5.1, “Replication Features and Issues”中描述的已知复制限制之一。如果是错误,请参阅 Section 19.5.5, “How to Report Replication Bugs or Problems”,了解如何报告。
如果在源上成功运行的语句在副本上拒绝运行,请尝试以下步骤,如果不可行,则无法通过删除副本的数据库并从源复制新快照进行完整数据库重新同步:
确定副本上受影响的表是否与源表不同。尝试理解是如何发生的。然后使副本的表与源的表相同,并运行START REPLICA
。
如果前面的步骤不起作用或不适用,请尝试理解是否可以安全地手动进行更新(如果需要),然后忽略源的下一个语句。
如果您决定副本可以跳过源的下一个语句,请发出以下语句:
mysql> SET GLOBAL sql_slave_skip_counter = *N*;
mysql> START SLAVE;
Or from MySQL 8.0.26:
mysql> SET GLOBAL sql_replica_skip_counter = *N*;
mysql> START REPLICA;
如果下一个来自源的语句不使用AUTO_INCREMENT
或LAST_INSERT_ID()
,则*N
*的值应为 1。否则,该值应为 2。对于使用AUTO_INCREMENT
或LAST_INSERT_ID()
的语句使用值 2 的原因是它们在源的二进制日志中占据两个事件。
参见 SET GLOBAL sql_slave_skip_counter 语法。
如果您确定副本最初与源完全同步,并且没有人在复制线程之外更新涉及的表,则差异可能是由错误引起的。如果您正在运行最新版本的 MySQL,请报告问题。如果您正在运行旧版本,请尝试升级到最新的生产版本以确定问题是否仍然存在。
当确定没有用户错误涉及,并且复制仍然无法正常工作或不稳定时,是时候向我们发送 bug 报告了。我们需要尽可能多地从您那里获取信息以便追踪 bug。请花些时间和精力准备一个好的 bug 报告。
如果您有一个可重现的测试用例来演示 bug,请按照第 1.5 节“如何报告 Bug 或问题”中给出的说明将其输入到我们的 bug 数据库中。如果您遇到“幻影”问题(即您无法随意复制的问题),请使用以下步骤:
验证没有用户错误涉及。例如,如果您在复制线程之外更新复制端,数据将不同步,更新时可能会出现唯一键冲突。在这种情况下,复制线程会停止并等待您手动清理表以将其带入同步状态。这不是一个复制问题。这是外部干扰导致复制失败的问题。
确保复制端正在运行并启用了二进制日志记录(log_bin
系统变量),并启用了 --log-slave-updates
选项,这会导致复制端将从源端接收的更新记录到自己的二进制日志中。这些设置是默认设置。
在重置复制状态之前,请保存所有证据。如果我们没有信息或只有零碎的信息,那么追踪问题将变得困难或不可能。您应该收集的证据包括:
SHOW MASTER STATUS
输出
SHOW REPLICA STATUS
输出
使用mysqlbinlog来检查二进制日志。以下内容应有助于找到问题陈述。*log_file
和log_pos
*是SHOW REPLICA STATUS
中的Master_Log_File
和Read_Master_Log_Pos
值。
$> mysqlbinlog --start-position=*log_pos* *log_file* | head
在收集了问题的证据之后,首先尝试将其作为一个独立的测试用例进行隔离。然后,按照第 1.5 节,“如何报告错误或问题”中的说明,尽可能多地输入问题信息到我们的错误数据库中。
目录
20.1 组复制背景
20.1.1 复制技术
20.1.2 组复制用例
20.1.3 多主和单主模式
20.1.4 组复制服务
20.1.5 组复制插件架构
20.2 开始使用
20.2.1 在单主模式下部署组复制
20.2.2 本地部署组复制
20.3 要求和限制
20.3.1 组复制要求
20.3.2 组复制限制
20.4 监控组复制
20.4.1 GTIDs 和组复制
20.4.2 组复制服务器状态
20.4.3 复制组成员表
20.4.4 复制组成员统计表
20.5 组复制操作
20.5.1 配置在线组
20.5.2 重新启动组
20.5.3 事务一致性保证
20.5.4 分布式恢复
20.5.5 支持 IPv6 和混合 IPv6 和 IPv4 组
20.5.6 使用 MySQL 企业备份与组复制
20.6 组复制安全
20.6.1 用于连接安全管理的通信堆栈
20.6.2 使用安全套接字层(SSL)保护组通信连接
20.6.3 保护分布式恢复连接
20.6.4 组复制 IP 地址权限
20.7 组复制性能和故障排除
20.7.1 调整组通信线程
20.7.2 流量控制
20.7.3 单一一致性领导者
20.7.4 消息压缩
20.7.5 消息分段
20.7.6 XCom 缓存管理
20.7.7 对故障检测和网络分区的响应
20.7.8 处理网络分区和失去法定人数
20.7.9 使用 Performance Schema Memory Instrumentation 监控 Group Replication 内存使用
20.8 升级 Group Replication
20.8.1 在组中组合不同的成员版本
20.8.2 Group Replication 离线升级
20.8.3 Group Replication 在线升级
20.9 Group Replication 变量
20.9.1 Group Replication 系统变量
20.9.2 Group Replication 状态变量
20.10 常见问题解答
本章介绍了 MySQL Group Replication 以及如何安装、配置和监控组。MySQL Group Replication 可以创建弹性、高可用、容错的复制拓扑。
组可以以单一主模式运行,具有自动主选举功能,每次只有一个服务器接受更新。另外,组也可以以多主模式部署,所有服务器都可以接受更新,即使它们同时发出。
有一个内置的组成员服务,保持组的视图在任何给定时间点对所有服务器一致和可用。服务器可以离开和加入组,视图会相应更新。有时服务器可能意外离开组,此时故障检测机制会检测到并通知组视图已更改。这一切都是自动的。
Group Replication 保证数据库服务持续可用。然而,重要的是要理解,如果组中的一个成员不可用,连接到该组成员的客户端必须被重定向或故障转移到组中的另一台服务器,使用连接器、负载均衡器、路由器或某种中间件。Group Replication 没有内置的方法来做到这一点。例如,参见 MySQL Router 8.0。
组复制作为 MySQL Server 的一个插件提供。您可以按照本章的说明在您想要的每个服务器实例上配置插件,启动组,并监视和管理组。部署 MySQL 服务器实例组的另一种方法是使用 InnoDB Cluster。
提示
要部署多个 MySQL 实例,您可以使用 InnoDB Cluster,它使您能够轻松管理一组 MySQL 服务器实例在 MySQL Shell 中。InnoDB Cluster 将 MySQL Group Replication 包装在一个编程环境中,使您可以轻松部署一组 MySQL 实例以实现高可用性。此外,InnoDB Cluster 与 MySQL Router 无缝接口,使您的应用程序可以连接到集群而无需编写自己的故障转移过程。然而,对于不需要高可用性的类似用例,您可以使用 InnoDB ReplicaSet。有关 MySQL Shell 的安装说明,请参见这里。
本章结构如下:
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-background.html
20.1.1 Replication Technologies
20.1.2 Group Replication Use Cases
20.1.3 Multi-Primary and Single-Primary Modes
20.1.4 Group Replication Services
20.1.5 Group Replication Plugin Architecture
本节提供了关于 MySQL Group Replication 的背景信息。
创建容错系统最常见的方法是通过使组件冗余化,换句话说,组件可以被移除而系统应该继续按预期运行。这带来了一系列挑战,将这些系统的复杂性提升到一个全新的水平。具体来说,复制的数据库必须处理这样一个事实,即它们需要维护和管理多台服务器,而不仅仅是一台。此外,由于服务器合作创建组,因此必须处理一些其他经典的分布式系统问题,例如网络分区或脑裂场景。
因此,最终的挑战是将数据库和数据复制的逻辑与多台服务器以一致且简单的方式协调的逻辑融合在一起。换句话说,让多台服务器就系统状态和每次系统经历的变化的数据达成一致意见。这可以总结为使服务器就每个数据库状态转换达成一致意见,以便它们都像一个单一数据库一样前进,或者最终收敛到相同的状态。这意味着它们需要作为(分布式)状态机运行。
MySQL Group Replication 提供了具有强大协调功能的分布式状态机复制。当服务器属于同一组时,它们会自动协调。该组可以在单主模式下运行,具有自动主选举,每次只有一台服务器接受更新。或者,对于更高级的用户,该组可以部署在多主模式下,其中所有服务器都可以接受更新,即使它们同时发出。这种功能是以应用程序必须解决这些部署所施加的限制为代价的。
存在一个内置的组成员服务,使组的视图在任何给定时间点对所有服务器保持一致和可用。服务器可以离开和加入组,视图会相应更新。有时服务器可能意外离开组,此时故障检测机制会检测到这一点,并通知组视图已更改。这一切都是自动的。
要使交易提交,大多数组成员必须就给定交易在全局交易序列中的顺序达成一致。决定提交或中止交易是每个服务器单独完成的,但所有服务器都做出相同的决定。如果出现网络分区,导致成员无法达成一致,那么系统将在解决此问题之前不会继续进行。因此,系统还具有内置的、自动的、防止脑裂的机制。
所有这些都由提供的群组通信系统(GCS)协议驱动。这些协议提供了故障检测机制、群组成员服务以及安全和完全有序的消息传递。所有这些属性对于创建一个确保数据在服务器组中一致复制的系统至关重要。在这项技术的核心是 Paxos 算法的实现。它充当群组通信引擎。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-replication-technologies.html
20.1.1.1 源到副本复制
20.1.1.2 集群复制
在深入了解 MySQL 集群复制之前,本节介绍了一些背景概念以及工作原理的概述。这提供了一些背景信息,帮助理解集群复制所需的条件以及经典异步 MySQL 复制与集群复制之间的区别。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-primary-secondary-replication.html
传统的 MySQL 复制 提供了一种简单的源到副本的复制方法。源是主服务器,副本是从服务器。源应用事务,提交它们,然后稍后(因此是异步的)将它们发送到副本以重新执行(在基于语句的复制中)或应用(在基于行的复制中)。这是一个共享无系统,所有服务器默认都有完整的数据副本。
图 20.1 MySQL 异步复制
还有半同步复制,它在协议中添加了一个同步步骤。这意味着主服务器在应用时等待副本确认已接收事务。只有在副本确认接收事务后,主服务器才恢复提交操作。
图 20.2 MySQL 半同步复制
在这两幅图中有一个经典的异步 MySQL 复制协议的图示(以及其半同步变体)。不同实例之间的箭头代表服务器之间或服务器与客户端应用程序之间交换的消息。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-summary.html
Group Replication 是一种可用于实现容错系统的技术。复制组是一组每个服务器都拥有自己完整数据副本的服务器(共享无内容复制方案),并通过消息传递相互交互。通信层提供了一组保证,如原子消息和总顺消息传递。这些是非常强大的属性,可以转化为非常有用的抽象,可以用来构建更高级的数据库复制解决方案。
MySQL Group Replication 建立在这些属性和抽象之上,并实现了一个多源更新的复制协议。一个复制组由多个服务器组成,组中的每个服务器可以随时独立执行事务。然而,所有读写事务只有在组批准后才提交。换句话说,对于任何读写事务,组都需要决定是否提交,因此提交操作不是来自原始服务器的单方面决定。只读事务在组内不需要协调,立即提交。
当一个读写事务在原始服务器准备提交时,服务器会原子地广播写入值(已更改的行)和相应的写入集(已更新行的唯一标识符)。由于事务通过原子广播发送,组中的所有服务器都会接收到事务,或者都不会接收到。如果它们接收到了,那么它们都会按照与之前发送的其他事务相同的顺序接收它。因此,所有服务器以相同的顺序接收相同的事务集,并为事务建立了全局总顺序。
然而,在不同服务器上并发执行的事务之间可能存在冲突。这种冲突是通过检查和比较两个不同且并发事务的写入集来检测的,在一个称为认证的过程中。在认证过程中,冲突检测是在行级别进行的:如果两个并发事务,在不同服务器上执行,更新了相同的行,则存在冲突。冲突解决程序规定,首先被排序的事务在所有服务器上提交,而第二个被排序的事务中止,因此在原始服务器上回滚,并被组中的其他服务器丢弃。例如,如果 t1 和 t2 在不同站点并发执行,都更改了相同的行,并且 t2 在 t1 之前被排序,那么 t2 赢得冲突,t1 被回滚。实际上,这是一个分布式的“先提交者获胜”规则。请注意,如果两个事务往往会发生冲突,那么将它们放在同一服务器上开始是一个好的做法,这样它们有机会在本地锁管理器上同步,而不是由于认证的结果而被回滚。
为了应用和外部化经过认证的事务,集群复制允许服务器偏离事务的约定顺序,如果这不会破坏一致性和有效性。集群复制是一个最终一致性系统,意味着一旦传入流量减少或停止,所有组成员都具有相同的数据内容。在流量流动时,事务可以以稍微不同的顺序外部化,或者在某些成员之前外部化。例如,在多主模式下,一个本地事务可能会在认证后立即外部化,尽管一个在全局顺序中较早的远程事务尚未被应用。当认证过程已经确定事务之间没有冲突时,这是允许的。在单主模式下,在主服务器上,存在一个小概率,即并发的、非冲突的本地事务可能会按照与集群复制约定的全局顺序不同的顺序提交和外部化。在不接受来自客户端的写入的从属服务器上,事务总是按照约定的顺序提交和外部化。
以下图描述了 MySQL 集群复制协议,并通过将其与 MySQL 复制(甚至 MySQL 半同步复制)进行比较,您可以看到一些差异。为了清晰起见,这张图片中省略了一些底层共识和 Paxos 相关的消息。
图 20.3 MySQL 集群复制协议
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-use-cases.html
Group Replication 可以让您创建具有冗余性的容错系统,通过将系统状态复制到一组服务器。即使其中一些服务器随后失败,只要不是全部或大多数,系统仍然可用。根据失败的服务器数量,群组可能会有性能或可伸缩性下降,但仍然可用。服务器故障是隔离的和独立的。它们由一个依赖于分布式故障检测器的群组成员服务跟踪,该故障检测器能够在任何服务器离开群组时发出信号,无论是自愿离开还是由于意外停止。有一个分布式恢复过程,确保当服务器加入群组时,它们会自动更新。不需要服务器故障转移,而多源更新到处的特性确保即使单个服务器故障,更新也不会被阻塞。总之,MySQL Group Replication 保证数据库服务持续可用。
重要的是要理解,尽管数据库服务可用,但在发生意外服务器退出时,连接到它的客户端必须被重定向或故障转移到另一台服务器。这不是 Group Replication 试图解决的问题。连接器、负载均衡器、路由器或某种形式的中间件更适合处理这个问题。例如,请参阅 MySQL Router 8.0。
总结一下,MySQL Group Replication 提供了一个高可用、高弹性、可靠的 MySQL 服务。
提示
要部署多个 MySQL 实例,您可以使用 InnoDB Cluster,它使您能够在 MySQL Shell 中轻松管理一组 MySQL 服务器实例。InnoDB Cluster 在一个编程环境中封装了 MySQL Group Replication,使您可以轻松部署一组 MySQL 实例以实现高可用性。此外,InnoDB Cluster 与 MySQL Router 无缝接口,使您的应用程序可以连接到集群而无需编写自己的故障转移过程。然而,对于不需要高可用性的类似用例,您可以使用 InnoDB ReplicaSet。MySQL Shell 的安装说明可以在这里找到。
以下示例是 Group Replication 的典型用例。
20.1.3.1 单主模式
20.1.3.2 多主模式
Group Replication 可以在单主模式或多主模式下运行。该组的模式是一个全组配置设置,由group_replication_single_primary_mode
系统变量指定,所有成员必须相同。ON
表示单主模式,这是默认模式,OFF
表示多主模式。不可能让组的成员以不同模式部署,例如一个成员配置为多主模式,而另一个成员处于单主模式。
在 Group Replication 运行时无法手动更改group_replication_single_primary_mode
的值。从 MySQL 8.0.13 开始,您可以使用group_replication_switch_to_single_primary_mode()
和group_replication_switch_to_multi_primary_mode()
函数在 Group Replication 仍在运行时将组从一种模式转换到另一种模式。这些函数管理更改组模式的过程,并确保数据的安全性和一致性。在早期版本中,要更改组的模式,您必须停止 Group Replication 并在所有成员上更改group_replication_single_primary_mode
的值。然后执行组的完全重启(由具有group_replication_bootstrap_group=ON
的服务器引导)以实施对新操作配置的更改。您无需重新启动服务器。
无论部署模式如何,Group Replication 不处理客户端故障转移。这必须由中间件框架(如 MySQL Router 8.0)、代理、连接器或应用程序本身处理。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-single-primary-mode.html
在单主模式下(group_replication_single_primary_mode=ON
),组具有一个被设置为读写模式的单主服务器。组中的所有其他成员都被设置为只读模式(使用super_read_only=ON
)。主服务器通常是第一个引导组的服务器。加入组的所有其他服务器都会了解主服务器并自动设置为只读模式。
在单主模式下,组复制要求只有一个服务器写入组,因此与多主模式相比,一致性检查可以更宽松,DDL 语句不需要额外小心处理。选项group_replication_enforce_update_everywhere_checks
用于启用或禁用组的严格一致性检查。在部署单主模式或将组更改为单主模式时,必须将此系统变量设置为OFF
。
被指定为主服务器的成员可以通过以下方式更改:
group_replication_set_as_primary()
函数指定特定成员作为新的主服务器。
group_replication_switch_to_single_primary_mode()
函数将运行在多主模式下的组更改为单主模式,新的主服务器将自动选举产生,或者您可以通过该函数指定新的主服务器。
这些函数只能在所有组成员运行 MySQL 8.0.13 或更高版本时使用。当新的主服务器自动选举产生或手动指定时,它会自动设置为读写模式,其他组成员保持为从服务器,因此为只读模式。图 20.4, “新主服务器选举”展示了这个过程。
图 20.4 新主服务器选举
当新的主要被选举或任命时,它可能有一些在旧主要上已应用但尚未应用在此服务器上的更改积压。在这种情况下,直到新的主要赶上旧的主要,读写事务可能会导致冲突并被回滚,只读事务可能会导致过时读取。如果激活并正确调整了 Group Replication 的流量控制机制,它将减少快速和慢速成员之间的差异,从而降低发生这种情况的机会。有关流量控制的更多信息,请参见第 20.7.2 节,“流量控制”。从 MySQL 8.0.14 开始,您还可以使用group_replication_consistency
系统变量来配置组的事务一致性级别,以防止此问题。设置BEFORE_ON_PRIMARY_FAILOVER
(或任何更高的一致性级别)将新事务保留在新选举的主要上,直到积压被应用。有关事务一致性的更多信息,请参见第 20.5.3 节,“事务一致性保证”。如果组未使用流量控制和事务一致性保证,等待新主要应用其与复制相关的中继日志再将客户端应用程序重新路由到它是一个良好的做法。
自动主要成员选举过程涉及每个成员查看组的新视图,对潜在的新主要成员进行排序,并选择符合条件的最合适的成员。每个成员在本地做出自己的决定,遵循其 MySQL Server 版本中的主要选举算法。因为所有成员必须达成相同的决定,如果其他组成员运行较低版本的 MySQL Server,则成员会调整其主要选举算法,以使其与组中具有最低 MySQL Server 版本的成员具有相同的行为。
成员在选举主要时考虑的因素按顺序如下:
group_replication_member_weight
系统变量指定。如果组中的任何成员正在运行 MySQL 服务器 5.7,在那里这个系统变量不可用,则忽略此因素。
group_replication_member_weight
系统变量指定一个范围为 0-100 的数字。所有成员的默认权重为 50,因此将权重设置为低于此值以降低其排序,将权重设置为高于此值以增加其排序。您可以使用此加权函数来优先使用更好的硬件或确保在主服务器计划维护期间故障转移到特定成员。
server_uuid
系统变量指定。具有最低服务器 UUID 的成员被选择为主服务器。此因素充当保证和可预测的决定者,以便如果没有任何重要因素可以确定,则所有组成员达成相同的决定。
要找出在单主模式下当前是主服务器的服务器,请使用performance_schema.replication_group_members
表中的MEMBER_ROLE
列。例如:
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;
+-------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-------------------------+-------------+
| remote1.example.com | PRIMARY |
| remote2.example.com | SECONDARY |
| remote3.example.com | SECONDARY |
+-------------------------+-------------+
警告
group_replication_primary_member
状态变量已被弃用,并计划在将来的版本中删除。
或者使用group_replication_primary_member
状态变量。
mysql> SHOW STATUS LIKE 'group_replication_primary_member'
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-multi-primary-mode.html
在多主模式(group_replication_single_primary_mode=OFF
)中,没有任何成员担任特殊角色。任何与其他组成员兼容的成员在加入组时被设置为读写模式,并且可以处理写事务,即使这些事务是并发发出的。
如果某个成员停止接受写事务,例如,出现意外服务器退出的情况,连接到该成员的客户端可以被重定向或故障转移至任何处于读写模式的其他成员。Group Replication 本身不处理客户端故障转移,因此您需要使用中间件框架(如 MySQL Router 8.0)、代理、连接器或应用程序本身来安排这一点。图 20.5,“客户端故障转移” 显示了客户端如何在成员离开组时重新连接到另一个组成员。
图 20.5 客户端故障转移
Group Replication 是一个最终一致性系统。这意味着一旦传入流量减少或停止,所有组成员都具有相同的数据内容。在流量流动时,事务可以在某些成员上外部化,然后在其他成员上,特别是如果某些成员的写吞吐量较低,则可能导致过时读取的可能性。在多主模式下,较慢的成员也可能积累过多的待认证和应用的事务,导致冲突和认证失败的风险增加。为了限制这些问题,您可以激活和调整 Group Replication 的流量控制机制,以最小化快速和慢速成员之间的差异。有关流量控制的更多信息,请参见第 20.7.2 节,“流量控制”。
从 MySQL 8.0.14 开始,如果您希望对组中的每个事务都有事务一致性保证,可以使用group_replication_consistency
系统变量来实现。您可以选择适合组工作负载和数据读写优先级的设置,考虑到增加一致性所需的同步对性能的影响。您还可以为单个会话设置系统变量,以保护特别关注并发性的事务。有关事务一致性的更多信息,请参见 Section 20.5.3, “Transaction Consistency Guarantees”。
在多主模式下部署组时,会对事务进行检查以确保其与模式兼容。当在多主模式下部署 Group Replication 时,将进行以下严格的一致性检查:
这些检查由group_replication_enforce_update_everywhere_checks
系统变量控制。在多主模式下,该系统变量通常应设置为ON
,但可以通过将系统变量设置为OFF
来选择性地停用检查。在部署单主模式时,该系统变量必须设置为OFF
。
在多主模式下部署 Group Replication 拓扑时,在执行数据定义语句时,也通常称为数据定义语言(DDL),需要注意。
MySQL 8.0 引入了对原子数据定义语言(DDL)语句的支持,其中完整的 DDL 语句要么作为单个原子事务提交,要么回滚。然而,DDL 语句,无论是否原子,都会隐式结束当前会话中的任何活动事务,就好像在执行该语句之前执行了COMMIT
一样。这意味着 DDL 语句不能在另一个事务内执行,在事务控制语句(如START TRANSACTION ... COMMIT
)内执行,或者与同一事务内的其他语句组合。
Group Replication 基于一种乐观的复制范式,其中语句会乐观地执行,如果需要则会回滚。每个服务器在未先获得组协议的情况下执行。因此,在多主模式下复制 DDL 语句时需要更加小心。如果对同一对象进行模式更改(使用 DDL)和对对象包含的数据进行更改(使用 DML),则这些更改需要在模式操作尚未完成并在所有地方复制之前通过同一服务器处理。如果未能这样做,当操作被中断或仅部分完成时可能导致数据不一致。如果组部署在单主模式下,则不会出现此问题,因为所有更改都通过同一服务器执行,即主服务器。
有关 MySQL 8.0 中原子 DDL 支持的详细信息,以及某些语句复制行为的变化,请参阅 第 15.1.1 节,“原子数据定义语句支持”。
为了获得最佳兼容性和性能,组中的所有成员应该运行相同版本的 MySQL Server,因此也应该运行相同版本的 Group Replication。在多主模式下,这更为重要,因为所有成员通常会以读写模式加入组。如果一个组包含运行不止一个 MySQL Server 版本的成员,那么一些成员可能与其他成员不兼容,因为它们支持其他成员不支持的功能,或者缺少其他成员拥有的功能。为了防范这种情况,在新成员加入时(包括已升级并重新启动的前成员),该成员会对其与组中其他成员的兼容性进行检查。
这些兼容性检查的一个重要结果在多主模式下尤为重要。如果加入的成员运行的 MySQL Server 版本高于现有组成员运行的最低版本,它会加入组但保持为只读模式。(在单主模式下运行的组中,新添加的成员默认为只读模式。)运行 MySQL 8.0.17 或更高版本的成员在检查兼容性时会考虑发布的补丁版本。运行 MySQL 8.0.16 或更低版本,或 MySQL 5.7 的成员只考虑主要版本。
在以多主模式运行的组中,成员使用不同的 MySQL Server 版本,Group Replication 会自动管理运行 MySQL 8.0.17 或更高版本的成员的读写和只读状态。如果一个成员离开组,那些运行当前最低版本的成员会自动设置为读写模式。当您将以单主模式运行的组更改为以多主模式运行时,使用group_replication_switch_to_multi_primary_mode()
函数,Group Replication 会自动将成员设置为正确的模式。如果成员运行的 MySQL 服务器版本高于组中最低版本,则会自动将其置于只读模式,而运行最低版本的成员会置于读写模式。
有关组中版本兼容性的完整信息以及在升级过程中如何影响组行为,请参阅第 20.8.1 节,“在组中组合不同的成员版本”。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-details.html
20.1.4.1 组成员资格
20.1.4.2 失效检测
20.1.4.3 容错性
20.1.4.4 可观察性
本节介绍了组复制构建的一些服务。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-group-membership.html
在 MySQL Group Replication 中,一组服务器形成一个复制组。群组有一个以 UUID 形式的名称。群组是动态的,服务器可以随时离开(自愿或非自愿)和加入。每当服务器加入或离开时,群组会自动调整自身。
如果一个服务器加入群组,它会通过从现有服务器获取缺失状态来自动更新自身。如果一个服务器离开群组,例如因为维护而关闭,剩余的服务器会注意到它已经离开,并自动重新配置群组。
Group Replication 具有一个群组成员服务,定义了哪些服务器在线并参与群组。在线服务器列表被称为视图。群组中的每个服务器在给定时间点都对哪些服务器是活跃参与群组的成员有一致的视图。
群组成员不仅必须就事务提交达成一致意见,还必须就当前视图达成一致。如果现有成员同意新服务器应该成为群组的一部分,群组将重新配置以将该服务器整合进去,从而触发视图更改。如果一个服务器离开群组,无论是自愿还是非自愿,群组会动态重新排列其配置,并触发视图更改。
当一个成员自愿离开群组时,首先启动动态群重新配置,在此期间,所有成员必须就没有离开的服务器达成新的共识。然而,如果一个成员非自愿离开群组,例如因为意外停止或网络连接中断,它无法启动重新配置。在这种情况下,Group Replication 的故障检测机制在短时间内识别出成员已经离开,并提出了一个没有失败成员的群组重新配置。与自愿离开的成员一样,重新配置需要群组中大多数服务器的同意。然而,如果群组无法达成一致意见,例如因为分区导致没有大多数服务器在线,系统无法动态更改配置,并阻止分裂脑的情况发生。这种情况需要管理员干预。
一个成员可能会短暂离线,然后在故障检测机制检测到其故障之前,尝试重新加入组,而在组被重新配置以移除该成员之前。在这种情况下,重新加入的成员会忘记其先前的状态,但如果其他成员向其发送旨在其崩溃前状态的消息,这可能会导致问题,包括可能的数据不一致性。如果处于这种情况的成员参与 XCom 的共识协议,它有可能导致 XCom 在同一共识轮中传递不同的值,因为在故障前后做出不同的决定。
为了应对这种可能性,在 MySQL 5.7.22 版本和 MySQL 8.0 版本中,Group Replication 会检查这样一种情况:当同一台服务器的新实例尝试加入组时,而其旧实例(具有相同的地址和端口号)仍然被列为成员。新实例将被阻止加入组,直到旧实例通过重新配置被移除。请注意,如果group_replication_member_expel_timeout
系统变量添加了等待时间,以允许成员在被驱逐之前重新连接到组,那么受到怀疑的成员在怀疑超时之前重新连接到组,可以作为其当前实例再次活跃在组中。当成员超过驱逐超时并被驱逐出组,或者当服务器上的 Group Replication 被STOP GROUP_REPLICATION
语句或服务器故障停止时,它必须作为新实例重新加入。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-failure-detection.html
Group Replication 的故障检测机制是一个分布式服务,能够识别组中的服务器是否与其他服务器通信,因此被怀疑处于停机状态。如果组的共识是怀疑可能是真实的,组将协调决定将该成员驱逐。驱逐一个不通信的成员是必要的,因为组需要大多数成员就交易或视图更改达成一致。如果一个成员不参与这些决定,组必须将其移除,以增加组中包含大多数正常工作成员的机会,从而可以继续处理交易。
在复制组中,每个成员与其他成员之间都有一条点对点通信通道,形成一个完全连接的图。这些连接由组通信引擎(XCom,一种 Paxos 变体)管理,并使用 TCP/IP 套接字。一个通道用于向成员发送消息,另一个通道用于接收来自成员的消息。如果一个成员在 5 秒内没有收到来自另一个成员的消息,它会怀疑该成员已经失败,并在自己的性能模式表replication_group_members
中将该成员的状态列为UNREACHABLE
。通常,两个成员会互相怀疑对方已经失败,因为它们彼此之间没有通信。但也有可能,尽管不太可能,成员 A 怀疑成员 B 已经失败,但成员 B 并没有怀疑成员 A 已经失败 - 可能是由于路由或防火墙问题。一个成员也可以怀疑自己。一个与组其他成员隔离的成员怀疑所有其他成员已经失败。
如果一个怀疑持续超过 10 秒,怀疑的成员会试图向组内其他成员传播其观点,即怀疑的成员有缺陷。只有当怀疑的成员是通知者时才会这样做,这是从其内部 XCom 节点编号计算出来的。如果一个成员实际上与组的其他成员隔离开来,它可能会尝试传播其观点,但这不会产生任何后果,因为它无法获得其他成员的多数同意。只有当一个成员是通知者时,怀疑持续时间足够长以传播到组的其他成员,并且其他成员同意时,怀疑才会产生后果。在这种情况下,怀疑的成员被标记为从组中驱逐,在group_replication_member_expel_timeout
系统变量设置的等待期过后,驱逐机制检测到并实施驱逐后被驱逐。
在网络不稳定且成员经常在不同组合中失去和重新建立连接的情况下,理论上可能导致一个组标记所有成员进行驱逐,之后该组将停止存在并需要重新设置。为了应对这种可能性,从 MySQL 8.0.20 开始,Group Replication 的 Group Communication System (GCS)跟踪已被标记为驱逐的组成员,并在决定是否存在多数时将其视为被怀疑成员的组。这确保至少有一个成员留在组内,使组能够继续存在。当一个被驱逐的成员实际上已被从组中移除时,GCS 将删除其被标记为驱逐的记录,以便该成员可以重新加入组(如果有可能)。
有关可以配置的 Group Replication 系统变量的信息,以指定工作组成员对故障情况的响应以及被怀疑已经发生故障的组成员采取的行动,请参见 Section 20.7.7, “Responses to Failure Detection and Network Partitioning”。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-fault-tolerance.html
MySQL Group Replication 基于 Paxos 分布式算法的实现,提供服务器之间的分布式协调。因此,它需要大多数服务器处于活动状态才能达成法定人数,从而做出决定。这直接影响系统在不损害自身和整体功能性的情况下能够容忍的故障数量。需要容忍f
个故障的服务器数量(n)为n = 2 x f + 1
。
实际上,这意味着为了容忍一个故障,组内必须有三台服务器。因此,如果一台服务器故障,仍然有两台服务器形成多数(三个中的两个),使系统能够继续自动做出决策并进展。然而,如果第二台服务器非自愿故障,那么组(只剩下一台服务器)将阻塞,因为没有多数达成决定。
以下是一个小表格,说明了上述公式。
组大小 | 多数 | 即时容忍的故障数 |
---|---|---|
1 | 1 | 0 |
2 | 2 | 0 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
7 | 4 | 3 |
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-observability.html
尽管 Group Replication 插件中内置了许多自动化功能,但有时您可能需要了解幕后发生的情况。这就是 Group Replication 和 Performance Schema 的仪表化变得重要的地方。整个系统的状态(包括视图、冲突统计和服务状态)可以通过 Performance Schema 表进行查询。复制协议的分布式性质以及服务器实例之间达成一致并因此在事务和元数据上进行同步使得检查组的状态变得更加简单。例如,您可以连接到组中的单个服务器,并通过在与 Group Replication 相关的 Performance Schema 表上发出 select 语句来获取本地和全局信息。有关更多信息,请参见 第 20.4 节,“监控 Group Replication”。 ,成员 A 怀疑成员 B 已经失败,但成员 B 并没有怀疑成员 A 已经失败 - 可能是由于路由或防火墙问题。一个成员也可以怀疑自己。一个与组其他成员隔离的成员怀疑所有其他成员已经失败。
如果一个怀疑持续超过 10 秒,怀疑的成员会试图向组内其他成员传播其观点,即怀疑的成员有缺陷。只有当怀疑的成员是通知者时才会这样做,这是从其内部 XCom 节点编号计算出来的。如果一个成员实际上与组的其他成员隔离开来,它可能会尝试传播其观点,但这不会产生任何后果,因为它无法获得其他成员的多数同意。只有当一个成员是通知者时,怀疑持续时间足够长以传播到组的其他成员,并且其他成员同意时,怀疑才会产生后果。在这种情况下,怀疑的成员被标记为从组中驱逐,在group_replication_member_expel_timeout
系统变量设置的等待期过后,驱逐机制检测到并实施驱逐后被驱逐。
在网络不稳定且成员经常在不同组合中失去和重新建立连接的情况下,理论上可能导致一个组标记所有成员进行驱逐,之后该组将停止存在并需要重新设置。为了应对这种可能性,从 MySQL 8.0.20 开始,Group Replication 的 Group Communication System (GCS)跟踪已被标记为驱逐的组成员,并在决定是否存在多数时将其视为被怀疑成员的组。这确保至少有一个成员留在组内,使组能够继续存在。当一个被驱逐的成员实际上已被从组中移除时,GCS 将删除其被标记为驱逐的记录,以便该成员可以重新加入组(如果有可能)。
有关可以配置的 Group Replication 系统变量的信息,以指定工作组成员对故障情况的响应以及被怀疑已经发生故障的组成员采取的行动,请参见 Section 20.7.7, “Responses to Failure Detection and Network Partitioning”。
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-fault-tolerance.html
MySQL Group Replication 基于 Paxos 分布式算法的实现,提供服务器之间的分布式协调。因此,它需要大多数服务器处于活动状态才能达成法定人数,从而做出决定。这直接影响系统在不损害自身和整体功能性的情况下能够容忍的故障数量。需要容忍f
个故障的服务器数量(n)为n = 2 x f + 1
。
实际上,这意味着为了容忍一个故障,组内必须有三台服务器。因此,如果一台服务器故障,仍然有两台服务器形成多数(三个中的两个),使系统能够继续自动做出决策并进展。然而,如果第二台服务器非自愿故障,那么组(只剩下一台服务器)将阻塞,因为没有多数达成决定。
以下是一个小表格,说明了上述公式。
组大小 | 多数 | 即时容忍的故障数 |
---|---|---|
1 | 1 | 0 |
2 | 2 | 0 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
7 | 4 | 3 |
原文:
dev.mysql.com/doc/refman/8.0/en/group-replication-observability.html
尽管 Group Replication 插件中内置了许多自动化功能,但有时您可能需要了解幕后发生的情况。这就是 Group Replication 和 Performance Schema 的仪表化变得重要的地方。整个系统的状态(包括视图、冲突统计和服务状态)可以通过 Performance Schema 表进行查询。复制协议的分布式性质以及服务器实例之间达成一致并因此在事务和元数据上进行同步使得检查组的状态变得更加简单。例如,您可以连接到组中的单个服务器,并通过在与 Group Replication 相关的 Performance Schema 表上发出 select 语句来获取本地和全局信息。有关更多信息,请参见 第 20.4 节,“监控 Group Replication”。