NDB Cluster 中的复制使用每个 MySQL Server 实例(作为集群中被复制的 SQL 节点以及副本中的 SQL 节点)上的 mysql
数据库中的多个专用表。无论副本是单个服务器还是集群,这都是正确的。
ndb_binlog_index
和 ndb_apply_status
表是在 mysql
数据库中创建的。用户不应显式复制它们。通常不需要用户干预来创建或维护这两个表,因为它们都由 NDB
二进制日志(binlog)注入线程维护。这使源 mysqld 进程保持更新以反映由 NDB
存储引擎执行的更改。 NDB
binlog 注入线程 直接从 NDB
存储引擎接收事件。 NDB
注入器负责捕获集群中的所有数据事件,并确保将所有更改、插入或删除数据的事件记录在 ndb_binlog_index
表中。副本 I/O(接收器)线程将事件从源的二进制日志传输到副本的中继日志。
ndb_replication
表必须手动创建。用户可以更新此表以按数据库或表进行过滤。有关更多信息,请参阅 ndb_replication 表。 ndb_replication
也用于 NDB 复制冲突检测和冲突解决控制;请参阅 冲突解决控制。
即使 ndb_binlog_index
和 ndb_apply_status
是自动创建和维护的,但在准备 NDB Cluster 进行复制时,建议检查这些表的是否存在和完整性。可以通过直接查询源上的 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
用于记录哪些 epoch 事务已从上游源复制并应用到副本集群。此信息在 NDB
在线备份中捕获,但(按设计)它不会由 ndb_restore 恢复。在某些情况下,将此信息恢复用于新设置可能会有所帮助;您可以通过使用 --with-apply-status
选项调用 ndb_restore 来执行此操作。有关更多信息,请参阅该选项的说明。
NDB Cluster 复制使用 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
存储引擎来创建此表。
将 ndb_binlog_index
转换为 InnoDB
后,可能需要额外的磁盘空间。如果这成为问题,您可以通过为该表使用 InnoDB
表空间,将其 ROW_FORMAT
更改为 COMPRESSED
或同时执行这两个操作来节省空间。有关更多信息,请参阅 第 15.1.21 节,“CREATE TABLESPACE 语句” 和 第 15.1.20 节,“CREATE TABLE 语句”,以及 第 17.6.3 节,“表空间”。
ndb_binlog_index
表的大小取决于每个二进制日志文件中的 epoch 数量和二进制日志文件的数量。每个二进制日志文件中的 epoch 数量通常取决于每个 epoch 生成的二进制日志数量和二进制日志文件的大小,较小的 epoch 会导致每个文件中的 epoch 更多。您应该注意,即使 --ndb-log-empty-epochs
选项为 OFF
,空 epoch 也会生成对 ndb_binlog_index
表的插入操作,这意味着每个文件中的条目数量取决于文件的使用时间;这种关系可以用以下公式表示
[number of epochs per file] = [time spent per file] / TimeBetweenEpochs
一个繁忙的 NDB Cluster 会定期写入二进制日志,并且可能比一个安静的 NDB Cluster 更快地轮换二进制日志文件。这意味着一个具有 --ndb-log-empty-epochs=ON
的 “安静” NDB Cluster 实际上可能比一个具有大量活动的 NDB Cluster 具有更高的每个文件中的 ndb_binlog_index
行数。
当使用 --ndb-log-orig
选项启动 mysqld 时,orig_server_id
和 orig_epoch
列分别存储事件起源的服务器的 ID 以及事件在起源服务器上发生的 epoch,这在采用多个源的 NDB Cluster 复制设置中很有用。用于在多源设置中查找副本上最高应用 epoch 的最近二进制日志位置的 SELECT
语句(请参阅 第 25.7.10 节,“NDB Cluster 复制:双向和循环复制”)使用了这两个未索引的列。这会导致尝试故障转移时出现性能问题,因为查询必须执行表扫描,尤其是在源一直在使用 --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 Cluster 复制实现故障转移”。
下图显示了 NDB Cluster 复制源服务器、其二进制日志注入线程和 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 ;仅在 before 图像中记录主键列,仅在 after 图像中记录更新的列(与 --ndb-log-update-minimal 相同的效果,但仅适用于一个或多个指定表) |
9 | 将更新记录为 UPDATE_ROW ;仅在 before 图像中记录主键列,在 after 图像中记录所有除主键列之外的列 |
binlog_type
值 4 和 5 未使用,因此从刚才显示的表以及下一个表中省略。
几个 binlog_type
值等效于 mysqld 日志记录选项 --ndb-log-updated-only
、--ndb-log-update-as-write
和 --ndb-log-update-minimal
的各种组合,如以下表格所示
表 25.43 binlog_type 值与 NDB 日志记录选项的等效组合
值 | --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 |
可以通过使用适当的 db
、table_name
和 binlog_type
列值将行插入 ndb_replication
表来将二进制日志记录设置为不同表的不同格式。在设置二进制日志记录格式时,应使用前面表格中显示的内部整数值。以下两个语句将表 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
选项的设置决定是否使用或不使用“before”图像来执行日志记录。
由于对更新和删除操作的冲突解决是在 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” 事件中。