NDB 8.4 中的 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
是平均周期大小(作为每个周期的操作数),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
事件(用于传送周期元数据)。这可以避免每个周期事务都简单地依赖于前一个周期,但确实要求在保留提交顺序的情况下将二进制日志应用于副本。这也意味着具有写入集依赖关系的 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。
如果二进制日志事务依赖于其直接前身,则其应用程序将被序列化。如果依赖关系位于较早的事务上,则可以与前面的独立事务并行应用该事务。
可以使用 mysqlbinlog 查看 ANONYMOUS_GTID
事件的内容,包括 sequence_number
和 last_committed
(以及事务依赖关系)。
源上生成的 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 二进制日志时,这可能会导致性能下降或其他问题。