25.7.11 NDB 集群复制使用多线程应用程序
NDB 复制在 NDB 8.4 中支持使用通用 MySQL 服务器多线程应用程序机制(MTA),该机制允许独立的二进制日志事务在副本上并行地被应用,从而提高峰值复制吞吐量。
MySQL 服务器 MTA 实现将单独的二进制日志事务委派给一个可配置的工作线程池(worker threads),并确保 worker threads 遵守二进制日志中的事务依赖关系,并维护 commit 顺序,如果需要(请参阅第 19.2.3 节,“Replication Threads”)。要在 NDB 集群中使用该功能, replica 必须被配置为使用多个工作线程。要实现这一点,请将replica_parallel_workers
设置来控制 replica 上的工作线程数量。默认值为 4。
如果在源mysqld上设置replica_parallel_type
,则必须将其设置为 LOGICAL_CLOCK
(默认值)。
NDB
不支持replica_parallel_type=DATABASE
。
此外,建议您将源服务器用于跟踪二进制日志事务写集的内存设置(binlog_transaction_dependency_history_size
)设置为
,其中E
* P
E
是平均epoch大小(以操作数为单位),P
是预期的最大并行度。请参阅 Writeset Tracking Memory Usage,了解更多信息。
MTA的NDB副本mysqld配置要求replica_parallel_workers
大于1。启用MTA时的推荐初始值为4,这也是默认值。
此外,replica_preserve_commit_order
必须设置为ON
。这也是默认值。
事务依赖关系是通过分析每个事务的写集来检测的,即该事务所写的行(表、键值)。如果两个事务修改同一行,他们被认为是相互依赖的,需要按顺序(即串行)应用,以避免死锁或错误结果。如果一个表具有secondary唯一键,这些值也将添加到事务的写集中,以检测不同事务对同一唯一键值的影响,并因此要求排序。对于无法高效确定依赖关系的情况,mysqld将退回到考虑事务相互依赖的安全原因。
事务依赖关系在源mysqld中被编码为二进制日志。依赖关系使用逻辑时钟(Logical clock)方案在ANONYMOUS_ GTID
事件中编码。 (见Section 19.1.4.1,“Replication Mode Concepts”。)
MySQL(和NDB集群)所使用的写集实现基于哈希冲突检测,基于相关表和索引值的64位行哈希值。这可靠地检测到同一键被看到两次,但也可能产生false positives,如果不同的表和索引值哈希到相同的64位值;这可能导致人为依赖关系,减少可用并发性。
事务依赖关系将被强制执行以下情况之一:
-
DDL语句
-
二进制日志轮转或遇到二进制日志文件边界
-
写入历史大小限制
-
引用目标表外键的写入操作
更具体地说,事务对父表(parent)进行插入、更新和删除操作时,相对于所有前置和后置事务都是串行化的,而不是只与涉及约束关系的表事务串行化。反之,对于引用外键的子表(child)进行插入、更新和删除操作的事务,不特别地与其他事务串行化。
MySQL MTA 实现尝试并行应用独立的二进制日志事务。NDB
记录在同一个 epoch(TimeBetweenEpochs
,默认 100 毫秒)中所有用户事务的更改,在一个二进制日志事务中,即 epoch 事务。因此,对于两个连续的 epoch 事务,如果它们之间没有修改任何行,则可以并行应用。如果在两个 epoch 中都修改了某个行,则它们是依赖的,需要串行应用,这可能会限制可exploitable 并行性。
epoch 事务根据源集群在 epoch 中修改的行集来判断是否独立,不包括传递 epoch 元数据的mysql.ndb_apply_status
WRITE_ROW
事件。这避免了每个 epoch 事务都对前一个 epoch 依赖,但需要在复制服务器上将 binlog 应用,以保持提交顺序。这也意味着 NDB 二进制日志具有写入历史依赖关系,不适合由使用不同 MySQL 存储引擎的副本数据库使用。
可能需要或有必要修改应用程序事务行为,以避免在短时间内对同一行重复修改,单独的事务,以提高可exploitable并行性。
使用binlog_transaction_dependency_history_size
服务器系统变量可以设置跟踪二进制日志事务写入集的内存大小,该变量默认为25000个行哈希。
如果平均二进制日志事务修改N
行,那么要能够识别独立的事务(可并行化)到并行性水平P
,我们需要binlog_transaction_dependency_history_size
至少为
。(最大值为1000000。)N
* P
有限的历史记录大小导致可靠确定的最大依赖长度,从而给出了可表达的有限并行性。任何未在历史记录中找到的一行可能依赖于最后一个从历史记录中 purged 的事务。
写入集历史记录不像滑动窗口,覆盖最后N
个事务,而是一个有限的缓冲区,当它满时将其内容完全丢弃。因此,历史大小随时间而变化,类似于 sawtooth 模式,因此独立的事务可能仍被标记为依赖,如果在它们被处理之间写入集历史记录缓冲区已经被重置。
在这个方案中,每个事务在二进制日志文件中都被注释了一个 sequence_number
(1, 2, 3,...) 和它依赖于的最 recent 二进制日志事务的序列号,称为 last_committed
。
在给定的二进制日志文件中,第一个事务的 sequence_number
是 1,last_committed
是 0。
如果一个二进制日志事务依赖于它的直接前驱,那么它的应用是 serialized。如果依赖于更早的事务,那么可能可以并行地应用该事务和前面的独立事务。
使用 mysqlbinlog 可以查看 ANONYMOUS_ GTID
事件的内容,包括 sequence_number
和 last_committed
(因此可以确定事务依赖关系)。
源服务器生成的 ANONYMOUS_ GTID
事件将单独处理与压缩的事务 payload,使用 bulk BEGIN
、TABLE_MAP*
、WRITE_ROW*
、UPDATE_ROW*
、DELETE_ROW*
和 COMMIT
事件,允许在解压缩之前确定依赖关系。这意味着复制协调线程可以将事务 payload 的解压缩委派给工作线程,从而自动并行地解压缩独立事务在复制服务器上。
次要唯一列。具有次要唯一列(即除了主键外的唯一键)的表格将所有列发送到源,以检测唯一键相关的冲突。
在当前二进制日志模式中,不包括所有列,只包括更改的列(--ndb-log-updated-only=OFF
,--ndb-log-update-minimal=ON
,--ndb-log-update-as-write=OFF
),这可能会增加数据节点到 SQL 节点的数据传输量。
影响取决于修改行(更新或删除)的速度,以及未实际修改的列中的数据体积。
NDB 到 InnoDB 的复制。 NDB
二进制日志注入器事务依赖关系跟踪故意忽略由生成的 mysql.ndb_apply_status
元数据事件创建的交互事务依赖关系,这些事件在副本应用程序中作为 epoch 事务的提交的一部分处理。对于到InnoDB
的复制,没有特殊处理;这可能会导致使用 InnoDB
多线程应用程序消费 NDB
MTA 二进制日志时出现性能下降或其他问题。