NDB 集群中的复制使用每个 MySQL 服务器实例的 mysql
数据库中的一系列专用表,这些实例作为集群中被复制的 SQL 节点和副本中的 SQL 节点。无论副本是单个服务器还是集群,情况都是如此。
ndb_binlog_index
和 ndb_apply_status
表在 mysql
数据库中创建。它们不应由用户显式复制。通常不需要用户干预来创建或维护这两个表,因为它们都由 NDB
二进制日志 (binlog) 注入线程维护。这使源 mysqld 进程能够更新由 NDB
存储引擎执行的更改。 NDB
的 二进制日志注入线程 直接从 NDB
存储引擎接收事件。 NDB
注入线程负责捕获集群中的所有数据事件,并确保所有更改、插入或删除数据的事件都被记录在 ndb_binlog_index
表中。副本 I/O(接收器)线程将事件从源的二进制日志传输到副本的中继日志。
ndb_replication
表必须手动创建。用户可以更新此表,以按数据库或表进行筛选。有关更多信息,请参见 ndb_replication 表。 ndb_replication
也用于 NDB 复制冲突检测和解决的冲突解决控制;请参见 冲突解决控制.
即使 ndb_binlog_index
和 ndb_apply_status
是自动创建和维护的,在准备 NDB 集群以进行复制时,建议检查这些表是否存在和完整性。可以通过直接查询源上的 mysql.ndb_binlog_index
表来查看记录在二进制日志中的事件数据。这也可以通过在源或副本 SQL 节点上使用 SHOW BINLOG EVENTS
语句来完成。(请参见 第 15.7.7.3 节,“SHOW BINLOG EVENTS 语句”。)
您还可以从 SHOW ENGINE NDB STATUS
的输出中获得有用的信息。
在对 NDB
表执行模式更改时,应用程序应等待发出该语句的 MySQL 客户端连接中的 ALTER TABLE
语句返回,然后再尝试使用该表的更新定义。
ndb_apply_status
用于记录从源复制到副本的操作。如果副本上不存在 ndb_apply_status
表,则 ndb_restore 会重新创建它。
与 ndb_binlog_index
不同的是,此表中的数据不特定于(副本)集群中的任何一个 SQL 节点,因此 ndb_apply_status
可以使用 NDBCLUSTER
存储引擎,如下所示
CREATE TABLE `ndb_apply_status` (
`server_id` INT(10) UNSIGNED NOT NULL,
`epoch` BIGINT(20) UNSIGNED NOT NULL,
`log_name` VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`start_pos` BIGINT(20) UNSIGNED NOT NULL,
`end_pos` BIGINT(20) UNSIGNED NOT NULL,
PRIMARY KEY (`server_id`) USING HASH
) ENGINE=NDBCLUSTER DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ndb_apply_status
表仅在副本上填充,这意味着该表在源上永远不会包含任何行;因此,没有必要在源上为 ndb_apply_status
分配任何 DataMemory
。
由于此表是根据源上发源的数据填充的,因此应允许其进行复制;任何有意阻止副本更新 ndb_apply_status
或阻止源写入二进制日志的复制筛选或二进制日志筛选规则可能会阻止集群之间复制正常运行。有关此类筛选规则可能导致的潜在问题的更多信息,请参见 NDB 集群之间复制的复制和二进制日志筛选规则.
可以删除此表,但不建议这样做。删除它会使所有 SQL 节点进入只读模式;NDB
检测到此表已被删除,并重新创建它,之后就可以再次执行更新。删除和重新创建 ndb_apply_status
会在二进制日志中创建一个间隙事件;间隙事件会导致副本 SQL 节点停止应用来自源的更改,直到复制通道重新启动。
此表中的 epoch
列中的 0
表示来自 NDB
以外的存储引擎的事务。
ndb_apply_status
用于记录哪些时期事务已从上游源复制并应用到副本集群。此信息捕获在 NDB
在线备份中,但(按设计)不会由 ndb_restore 恢复。在某些情况下,恢复此信息以用于新设置可能会有所帮助;您可以通过使用 ndb_restore 并使用 --with-apply-status
选项来执行此操作。有关更多信息,请参见该选项的说明。
NDB 集群复制使用 ndb_binlog_index
表来存储二进制日志的索引数据。由于此表是每个 MySQL 服务器的本地表,不参与集群,因此它使用 InnoDB
存储引擎。这意味着它必须在参与源集群的每个 mysqld 上分别创建。(二进制日志本身包含来自集群中所有 MySQL 服务器的更新。)此表定义如下
CREATE TABLE `ndb_binlog_index` (
`Position` BIGINT(20) UNSIGNED NOT NULL,
`File` VARCHAR(255) NOT NULL,
`epoch` BIGINT(20) UNSIGNED NOT NULL,
`inserts` INT(10) UNSIGNED NOT NULL,
`updates` INT(10) UNSIGNED NOT NULL,
`deletes` INT(10) UNSIGNED NOT NULL,
`schemaops` INT(10) UNSIGNED NOT NULL,
`orig_server_id` INT(10) UNSIGNED NOT NULL,
`orig_epoch` BIGINT(20) UNSIGNED NOT NULL,
`gci` INT(10) UNSIGNED NOT NULL,
`next_position` bigint(20) unsigned NOT NULL,
`next_file` varchar(255) NOT NULL,
PRIMARY KEY (`epoch`,`orig_server_id`,`orig_epoch`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
如果您要从较旧的版本升级,请执行 MySQL 升级过程,并确保通过使用 --upgrade=FORCE
选项启动 MySQL 服务器来升级系统表。系统表升级会导致 ALTER TABLE ... ENGINE=INNODB
语句为此表执行。出于向后兼容性的考虑,继续支持使用 MyISAM
存储引擎为该表进行存储。
在转换为 InnoDB
之后,ndb_binlog_index
可能需要额外的磁盘空间。如果这成为问题,您可以通过为此表使用 InnoDB
表空间、将它的 ROW_FORMAT
更改为 COMPRESSED
或两者来节省空间。有关更多信息,请参见 第 15.1.21 节,“CREATE TABLESPACE 语句” 和 第 15.1.20 节,“CREATE TABLE 语句”,以及 第 17.6.3 节,“表空间”.
ndb_binlog_index
表的大小取决于每个二进制日志文件中的时期数和二进制日志文件数。每个二进制日志文件中的时期数通常取决于每个时期生成的二进制日志量和二进制日志文件的大小,时期越小,每个文件中的时期数就越多。您应该注意,即使 --ndb-log-empty-epochs
选项为 OFF
,空时期也会为 ndb_binlog_index
表生成插入操作,这意味着每个文件中的条目数取决于文件的使用时间;此关系可以用以下公式表示
[number of epochs per file] = [time spent per file] / TimeBetweenEpochs
繁忙的 NDB 集群会定期写入二进制日志,并且可能比安静的集群更快地旋转二进制日志文件。这意味着 “安静” 的 NDB 集群,其中 --ndb-log-empty-epochs=ON
,实际上每个文件中的 ndb_binlog_index
行数可能比活动较多的集群要多得多。
当 mysqld 以 --ndb-log-orig
选项启动时,orig_server_id
和 orig_epoch
列分别存储事件起源的服务器的 ID 和事件在起源服务器上发生的时期,这在使用多个源的 NDB 集群复制设置中很有用。用于在多源设置中找到最接近副本上应用的最高时期的二进制日志位置的 SELECT
语句(请参见 第 25.7.10 节,“NDB 集群复制:双向和循环复制”)使用这两个列,它们没有被索引。这可能导致尝试故障转移时出现性能问题,因为查询必须执行表扫描,尤其是在源已使用 --ndb-log-empty-epochs=ON
运行时。您可以通过为这些列添加索引来提高多源故障转移时间,如下所示
ALTER TABLE mysql.ndb_binlog_index
ADD INDEX orig_lookup USING BTREE (orig_server_id, orig_epoch);
在从单个源复制到单个副本时,添加此索引没有任何好处,因为在这种情况下,用于获取二进制日志位置的查询不使用 orig_server_id
或 orig_epoch
。
有关使用 next_position
和 next_file
列的更多信息,请参见 第 25.7.8 节,“使用 NDB 集群复制实现故障转移”.
下图显示了 NDB 集群复制源服务器、它的二进制日志注入线程和 mysql.ndb_binlog_index
表之间的关系。
ndb_replication
表用于控制二进制日志记录和冲突解决,并针对每个表执行操作。此表中的每一行对应一个要复制的表,确定如何将更改记录到表中,以及是否指定了冲突解决函数,以及确定如何解决该表的冲突。
与 ndb_apply_status
和 ndb_replication
表不同,ndb_replication
表必须手动创建,使用此处所示的 SQL 语句。
CREATE TABLE mysql.ndb_replication (
db VARBINARY(63),
table_name VARBINARY(63),
server_id INT UNSIGNED,
binlog_type INT UNSIGNED,
conflict_fn VARBINARY(128),
PRIMARY KEY USING HASH (db, table_name, server_id)
) ENGINE=NDB
PARTITION BY KEY(db,table_name);
此表的列列于此处,并附有说明。
db
列包含要复制表的数据库的名称。
您可以使用通配符
_
和%
中的一个或两个作为数据库名称的一部分。(请参阅本节后面的 使用通配符匹配。)table_name
列要复制的表的名称。
表名可以包含通配符
_
和%
中的一个或两个。请参阅本节后面的 使用通配符匹配。server_id
列表所在 MySQL 实例(SQL 节点)的唯一服务器 ID。
此列中的
0
充当与%
等效的通配符,并匹配任何服务器 ID。(请参阅本节后面的 使用通配符匹配。)binlog_type
列要使用的二进制日志记录类型。请参阅文本以了解值和说明。
conflict_fn
列要应用的冲突解决函数;NDB$OLD()、NDB$MAX()、NDB$MAX_DELETE_WIN()、NDB$EPOCH()、NDB$EPOCH_TRANS()、NDB$EPOCH2()、NDB$EPOCH2_TRANS() NDB$MAX_INS() 或 NDB$MAX_DEL_WIN_INS() 中的一个;
NULL
表示此表不使用冲突解决。请参阅 冲突解决函数,以了解有关这些函数及其在 NDB 复制冲突解决中的使用的更多信息。
某些冲突解决函数(
NDB$OLD()
、NDB$EPOCH()
、NDB$EPOCH_TRANS()
)需要使用一个或多个用户创建的异常表。请参阅 冲突解决异常表。
要启用 NDB 复制的冲突解决,需要在应该解决冲突的 SQL 节点或节点上创建并填充此表,其中包含控制信息。根据要使用的冲突解决类型和方法,这可能是源服务器、副本服务器或两者。在一个简单的源-副本设置中,数据也可以在副本服务器上本地更改,这通常是副本服务器。在一个更复杂的复制方案中,例如双向复制,这通常是所有参与的源服务器。请参阅 第 25.7.12 节,“NDB 集群复制冲突解决”,以了解有关此内容的更多信息。
ndb_replication
表允许在二进制日志记录方面对表进行表级控制,超出了冲突解决的范围,在这种情况下,conflict_fn
被指定为 NULL
,而其余列值用于控制给定表或与通配符表达式匹配的表的集合的二进制日志记录。通过为 binlog_type
列设置适当的值,您可以使给定表或表的日志记录使用所需的二进制日志格式,或者完全禁用二进制日志记录。此列的可能值及其值和说明显示在下表中。
表 25.42 binlog_type 值,包括值和说明
值 | 说明 |
---|---|
0 | 使用服务器默认值 |
1 | 不要将此表记录在二进制日志中(与 sql_log_bin = 0 的效果相同,但仅适用于一个或多个指定的表) |
2 | 仅记录更新的属性;将这些属性记录为 WRITE_ROW 事件 |
3 | 记录完整行,即使没有更新(MySQL 服务器默认行为) |
6 | 使用更新的属性,即使值没有更改 |
7 | 记录完整行,即使没有值更改;将更新记录为 UPDATE_ROW 事件 |
8 | 将更新记录为 UPDATE_ROW ;在之前图像中仅记录主键列,在之后图像中仅记录更新的列(与 --ndb-log-update-minimal 的效果相同,但仅适用于一个或多个指定的表) |
9 | 将更新记录为 UPDATE_ROW ;在之前图像中仅记录主键列,在之后图像中记录所有非主键列 |
binlog_type
值 4 和 5 未使用,因此在下表中省略,以及在下一张表中省略。
多个 binlog_type
值等效于 mysqld 日志记录选项 --ndb-log-updated-only
、--ndb-log-update-as-write
和 --ndb-log-update-minimal
的各种组合,如下表所示。
表 25.43 与 NDB 日志记录选项组合等效的 binlog_type 值
值 | --ndb-log-updated-only 值 |
--ndb-log-update-as-write 值 |
--ndb-log-update-minimal 值 |
---|---|---|---|
0 | -- | -- | -- |
1 | -- | -- | -- |
2 | ON | ON | OFF |
3 | OFF | ON | OFF |
6 | ON | OFF | OFF |
7 | OFF | OFF | OFF |
8 | ON | OFF | ON |
9 | OFF | OFF | ON |
通过将行插入 ndb_replication
表并使用适当的 db
、table_name
和 binlog_type
列值,可以将不同表的二进制日志记录设置为不同的格式。在设置二进制日志记录格式时,应使用前面表格中显示的内部整数值。以下两个语句将表 test.a
的二进制日志记录设置为记录完整行(值 3),并将表 test.b
的二进制日志记录设置为仅记录更新(值 2)。
# Table test.a: Log full rows
INSERT INTO mysql.ndb_replication VALUES("test", "a", 0, 3, NULL);
# Table test.b: log updates only
INSERT INTO mysql.ndb_replication VALUES("test", "b", 0, 2, NULL);
要禁用一个或多个表的日志记录,请将 binlog_type
设置为 1,如下所示。
# Disable binary logging for table test.t1
INSERT INTO mysql.ndb_replication VALUES("test", "t1", 0, 1, NULL);
# Disable binary logging for any table in 'test' whose name begins with 't'
INSERT INTO mysql.ndb_replication VALUES("test", "t%", 0, 1, NULL);
禁用给定表的日志记录等效于设置 sql_log_bin = 0
,只是它单独应用于一个或多个表。如果 SQL 节点未为给定表执行二进制日志记录,则不会为这些表发送行更改事件。这意味着它不会接收所有更改并丢弃某些更改,而是不会订阅这些更改。
禁用日志记录可能出于多种原因有用,包括此处列出的原因。
不通过网络发送更改通常可以节省带宽、缓冲和 CPU 资源。
不记录更新非常频繁但价值不大表的更改非常适合于瞬态数据(例如会话数据),这些数据在集群完全失败的情况下可能相对不重要。
使用会话变量(或
sql_log_bin
)和应用程序代码,还可以记录(或不记录)某些 SQL 语句或类型的 SQL 语句;例如,在某些情况下,可能希望不在一个或多个表上记录 DDL 语句。出于性能原因,将复制流拆分为两个(或多个)二进制日志可以完成,例如需要将不同的数据库复制到不同的位置,对不同的数据库使用不同的二进制日志记录类型,等等。
使用通配符匹配。 为了不必要地为复制设置中的每个数据库、表和 SQL 节点的组合插入 ndb_replication
表中的行,NDB
支持在此表的 db
、table_name
和 server_id
列上使用通配符匹配。在 db
和 table_name
中分别使用的数据库和表名可以包含以下通配符中的一个或两个。
_
(下划线字符):匹配零个或多个字符%
(百分号):匹配单个字符
(这些与 MySQL LIKE
运算符支持的通配符相同。)
server_id
列支持 0
作为与 _
等效的通配符(匹配任何内容)。这在前面显示的示例中使用。
ndb_replication
表中的给定行可以使用通配符来匹配数据库名称、表名和服务器 ID 中的任何组合。如果表中有多个潜在匹配项,则将选择最佳匹配项,根据下表进行选择,其中 W 代表通配符匹配,E 代表精确匹配,质量 列中的值越大,匹配越好。
表 25.44 mysql.ndb_replication 表中列上的不同通配符和精确匹配组合的权重
db |
table_name |
server_id |
质量 |
---|---|---|---|
W | W | W | 1 |
W | W | E | 2 |
W | E | W | 3 |
W | E | E | 4 |
E | W | W | 5 |
E | W | E | 6 |
E | E | W | 7 |
E | E | E | 8 |
因此,对数据库名称、表名和服务器 ID 进行精确匹配被认为是最佳(最强的),而最弱(最差)的匹配是所有三个列的通配符匹配。在选择要应用的规则时,只考虑匹配的强度;行在表中的出现顺序对该确定没有影响。
记录完整行或部分行。 有两种基本的记录行方法,由 --ndb-log-updated-only
选项为 mysqld 设置的值决定。
记录完整行(选项设置为
ON
)仅记录已更新的列数据,即已设置值(无论该值是否实际更改)的列数据。这是默认行为(选项设置为
OFF
)。
通常,仅记录已更新的列就足够了,而且效率更高;但是,如果您需要记录完整行,可以通过将 --ndb-log-updated-only
设置为 0
或 OFF
来实现。
将更改的数据记录为更新。 MySQL 服务器的 --ndb-log-update-as-write
选项的设置决定是否使用或不使用“之前”图像进行日志记录。
因为更新和删除操作的冲突解决是在 MySQL 服务器的更新处理程序中完成的,所以有必要控制复制源执行的日志记录,以使更新为更新而不是写入;也就是说,更新被视为现有行的更改,而不是写入新行,即使这些更改替换了现有行。
默认情况下,此选项处于启用状态;换句话说,更新被视为写入操作。也就是说,默认情况下,更新以 write_row
事件的形式写入二进制日志,而不是以 update_row
事件的形式写入。
要禁用此选项,请使用 --ndb-log-update-as-write=0
或 --ndb-log-update-as-write=OFF
启动源 mysqld。当从 NDB 表复制到使用不同存储引擎的表时,您必须执行此操作;有关更多信息,请参阅 从 NDB 复制到其他存储引擎,以及 从 NDB 复制到非事务性存储引擎。
对于使用 NDB$MAX_INS()
或 NDB$MAX_DEL_WIN_INS()
的插入冲突解决,SQL 节点(即 mysqld 进程)可以将源集群上的行更新记录为 WRITE_ROW
事件,并启用 --ndb-log-update-as-write
选项以实现幂等性和最佳大小。这对于这些算法有效,因为它们都将 WRITE_ROW
事件映射到插入或更新,具体取决于行是否已经存在,并且所需的元数据(时间戳列的““after”” 图像)存在于““WRITE_ROW”” 事件中。