NDB 9.0 中的 NDB 复制支持使用通用 MySQL 服务器多线程应用程序机制 (MTA),该机制允许在副本上并行应用独立的二进制日志事务,从而提高峰值复制吞吐量。
要求
MySQL 服务器 MTA 实现将单独的二进制日志事务的处理委托给一个工作线程池(其大小可配置),并协调工作线程以确保遵守二进制日志中编码的事务依赖关系,并在需要时维护提交顺序(请参阅第 19.2.3 节“复制线程”)。要将此功能与 NDB 集群一起使用,需要将副本配置为使用多个工作线程。为此,请设置 replica_parallel_workers
以控制副本上的工作线程数。默认值为 4。
MTA 配置:源
如果在源 mysqld 上设置,则 replica_parallel_type
必须为 LOGICAL_CLOCK
(默认值)。
NDB
不支持 replica_parallel_type=DATABASE
。
此外,建议您将用于跟踪源上二进制日志事务写入集的内存量(binlog_transaction_dependency_history_size
)设置为
,其中 E
* P
E
是平均纪元大小(作为每个纪元的 operasi 数),P
是最大预期并行度。有关更多信息,请参阅写入集跟踪内存使用情况。
MTA 配置:副本
NDB
MTA 的副本 mysqld 配置要求 replica_parallel_workers
大于 1。首次启用 MTA 时,建议的起始值为 4,这也是默认值。
此外,replica_preserve_commit_order
必须为 ON
。这也是默认值。
事务依赖关系和写入集处理
事务依赖关系是通过分析每个事务的写入集来检测的,即事务写入的行集(表、键值)。如果两个事务修改同一行,则认为它们是依赖的,并且必须按顺序(换句话说,串行)应用以避免死锁或错误结果。如果表具有辅助唯一键,则还会将这些值添加到事务的写入集中,以检测由影响相同唯一键值的不同事务隐含存在事务依赖关系,因此需要排序的情况。如果无法有效确定依赖关系,则 mysqld 会出于安全原因回退到将事务视为依赖项。
事务依赖关系由源 mysqld 编码在二进制日志中。依赖关系使用称为“逻辑时钟”的方案在 ANONYMOUS_GTID
事件中编码。(请参阅第 19.1.4.1 节“复制模式概念”。)
MySQL(和 NDB 集群)采用的写入集实现使用基于匹配相关表和索引值的 64 位行哈希的基于哈希的冲突检测。这可以可靠地检测到何时两次看到同一个键,但也可能在不同的表和索引值哈希到同一个 64 位值时产生误报;这可能会导致人为的依赖关系,从而降低可用的并行度。
事务依赖关系由以下任何一项强制执行
DDL 语句
二进制日志轮换或遇到二进制日志文件边界
写入集历史大小限制
引用目标表中父外键的写入
更具体地说,在外键父表上执行插入、更新和删除的事务相对于所有前面和后面的事务进行序列化,而不仅仅是相对于那些影响约束关系中涉及的表的事务进行序列化。相反,在外键子表(引用)上执行插入、更新和删除的事务不会特别地相互序列化。
MySQL MTA 实现尝试并行应用独立的二进制日志事务。NDB
记录在一个纪元(TimeBetweenEpochs
,默认为 100 毫秒)中提交的所有用户事务中发生的所有更改,在一个二进制日志事务中,称为纪元事务。因此,为了使两个连续的纪元事务独立并有可能并行应用,需要在两个纪元中都没有修改任何行。如果在两个纪元中都修改了任何单行,则它们是依赖的,并且是串行应用的,这可能会限制可利用的并行度。
纪元事务的独立性基于源集群中纪元中修改的行集来考虑,但不包括生成的传递纪元元数据的 mysql.ndb_apply_status
WRITE_ROW
事件。这避免了每个纪元事务都简单地依赖于前一个纪元,但确实要求在副本上应用 binlog 时保留提交顺序。这也意味着具有写入集依赖项的 NDB 二进制日志不适合使用不同 MySQL 存储引擎的副本数据库使用。
可以或希望修改应用程序事务行为,以避免在短时间内重复修改相同行的模式(在单独的事务中),从而提高可利用的应用并行度。
写入集跟踪内存使用情况
可以使用服务器系统变量 binlog_transaction_dependency_history_size
设置用于跟踪二进制日志事务写入集的内存量,该变量默认为 25000 个行哈希。
如果平均二进制日志事务修改了 N
行,那么为了能够识别高达并行度级别 P
的独立(可并行)事务,我们需要 binlog_transaction_dependency_history_size
至少为
。(最大值为 1000000。)N
* P
历史记录的有限大小导致可以可靠确定的有限最大依赖长度,从而给出可以表达的有限并行度。历史记录中未找到的任何行都可能依赖于从历史记录中清除的最后一个事务。
写入集历史记录不像是在最后 N
个事务上的滑动窗口;相反,它是一个允许完全填满的有限缓冲区,当它变满时,它的内容将被完全丢弃。这意味着历史记录大小会随着时间的推移呈现锯齿形模式,因此最大可检测依赖长度也会随着时间的推移呈现锯齿形模式,这样,如果在处理独立事务之间重置了写入集历史记录缓冲区,则它们可能仍被标记为依赖项。
在这种方案中,二进制日志文件中的每个事务都用 sequence_number
(1、2、3...)以及它依赖的最新二进制日志事务的序列号进行注释,我们将其称为 last_committed
。
在给定的二进制日志文件中,第一个事务的 sequence_number
为 1,last_committed
为 0。
如果二进制日志事务依赖于它的直接前身,则它的应用程序是序列化的。如果依赖项位于较早的事务上,则可以与前面的独立事务并行应用该事务。
ANONYMOUS_GTID
事件的内容,包括 sequence_number
和 last_committed
(以及事务依赖关系),可以使用 mysqlbinlog 查看。
源上生成的 ANONYMOUS_GTID
事件与具有批量 BEGIN
、TABLE_MAP*
、WRITE_ROW*
、UPDATE_ROW*
、DELETE_ROW*
和 COMMIT
事件的压缩事务有效负载分开处理,允许在解压缩之前确定依赖关系。这意味着副本协调器线程可以将事务有效负载解压缩委托给工作线程,从而在副本上提供独立事务的自动并行解压缩。
已知限制
辅助唯一列。 具有辅助唯一列(即除主键以外的唯一键)的表会将所有列发送到源,以便可以检测到与唯一键相关的冲突。
如果当前的二进制日志记录模式不包括所有列,而只包括更改的列(--ndb-log-updated-only=OFF
、--ndb-log-update-minimal=ON
、--ndb-log-update-as-write=OFF
),这会增加从数据节点发送到 SQL 节点的数据量。
影响取决于此类表中行的修改率(更新或删除)以及未实际修改的列中的数据量。
将 NDB 复制到 InnoDB。 NDB
二进制日志注入器事务依赖关系跟踪有意忽略由生成的 mysql.ndb_apply_status
元数据事件创建的事务间依赖关系,这些事件在副本应用程序上提交纪元事务时作为其中一部分单独处理。对于复制到 InnoDB
,没有特殊的处理;当使用 InnoDB
多线程应用程序来使用 NDB
MTA 二进制日志时,这可能会导致性能下降或其他问题。