MySQL Cluster重启过程(4)

COMPLETED RESTORING OFF-LINE CONSISTENT DATABASE

完成将片段副本还原到一致的全局检查点后,我们现在将根据还原的数据开始重建有序索引。 在重建有序索引之后,我们准备将START_RECCONF发送到起始DBDIH。 START_RECCONF通过DBLQH代理发送,因此在所有DBLQH实例完成此阶段并使用START_RECCONF响应之前,它不会传递到DBDIH。

此时,在DBLQH实例中,我们已恢复节点中所有数据的一致但旧的变体。 仍然没有有序索引,仍然有很多工作要让节点再次与其他节点同步。 对于群集重新启动,可能是节点已完全准备就绪,但是可能某些节点仍需要与已恢复更新的全局检查点的节点同步。

然后,起始节点的DBDIH将启动接管过程,因为起始节点具有一致的片段副本。 我们将通过为我们将复制的每个片段副本发送PREPARE_COPY_FRAG_REQ来为复制阶段准备起始节点的DBLQH。 这是一个可以并行化的顺序过程。

接管片段副本的过程非常复杂。 它首先将PREPARE_COPY_FRAGREQ / CONF发送到起始DBLQH,然后我们将UPDATE_TOREQ / CONF发送到主DBDIH,以确保我们在接管开始之前锁定片段信息。 在接收到该片段锁定的确认之后,起始节点向所有节点发送UPDATE_FRAG_STATEREQ / CONF以将新节点包括在片段上的所有操作中。

完成此操作后,我们再次向主节点发送UPDATE_TOREQ / CONF以通知新状态并解锁片段信息上的锁定。 然后我们准备执行片段的实际复制。 这是通过将COPY_FRAGREQ / CONF发送到将复制数据的节点来完成的。 完成此复制后,我们将COPY_ACTIVEREQ / CONF发送到起始节点以激活片段副本。

接下来,我们再次向主服务器发送UPDATE_TOREQ / CONF,通知我们即将安装接管新片段副本的提交。 接下来,我们通过向所有节点发送UPDATE_FRAG_STATEREQ / CONF来提交新的片段副本,通知它们片段副本的复制完成。 最后,我们使用UPDATE_TOREQ / CONF向主节点发送另一个更新。 现在我们终于完成了这个片段的复制。

这个方案的想法是第一个UPDATE_FRAG_STATEREQ确保我们是片段上所有事务的一部分。 在执行COPY_FRAGREQ以逐行地将起始节点的片段副本与活动节点的片段副本同步之后,我们确信两个片段副本完全同步,我们可以执行新的UPDATE_FRAG_STATEREQ以确保所有节点都知道我们 完成了同步。

COMPLETED RESTORING ON-LINE NOT RECOVERABLE DATABASE

此时,我们通过在线一次添加一个片段来恢复数据库的在线变体。 数据库仍然无法恢复,因为我们尚未启用日志记录,并且起始节点中没有数据的本地检查点。

下一步是启用所有片段的日志记录,完成此步骤后,我们将END_TOREQ发送到主DBDIH。 此时,我们将等到本地检查点完成,其中涉及此节点。 最后,当本地检查点完成后,我们将END_TOCONF发送到起始节点,然后我们将发送START_COPYCONF,这将完成重启的这个阶段。

COMPLETED RESTORING ON-LINE RECOVERABLE DATABASE

此时我们已经设法恢复所有数据,并且我们已经将它带到了在线状态,现在我们还在启用日志记录时执行了本地检查点,因此现在起始节点中的数据也是可恢复的。 所以这意味着数据库现在再次完全联机。

完成NDB_STTOR阶段5之后,此处等待点中已同步的所有节点将再次启动,NDBCNTR将继续运行NDB_STTOR的阶段6。

在此阶段DBLQH,DBDICT和DBTC设置一些状态变量,指示现在启动已完成(它尚未完全完成,但这些模块运行所需的所有服务都已完成.DBDIH还启动全局检查点协议以进行集群启动/重启 它已成为主节点。

现在,在群集启动/重启的情况下,所有节点还有一个等待点。 STTOR阶段5的最后一步是SUMA,它读取已配置的节点,获取节点组成员,如果有节点重新启动,它会要求另一个节点重新创建它的订阅。

STTOR Phase 6

我们现在进入STTOR阶段6.在此阶段,NDBCNTR获取节点的节点组,DBUTIL获取systable id,准备一组操作供以后使用,并连接到TC以使其能够代表其他模块运行关键操作 稍后的。

STTOR Phase 7

接下来我们进入STTOR阶段7.DBDICT现在启动索引统计循环,该循环将在节点存在时运行。

QMGR将启动仲裁处理,以处理我们面临网络分区风险的情况。

BACKUP将更新磁盘检查点速度(重启期间有一个配置变量用于速度,一个用于正常操作,这里我们安装正常运行速度)。 如果初始启动BACKUP也将通过DBUTIL创建备份序列。

如果SUMA在主节点中运行并且它是初始启动,它将创建一个序列。 SUMA还将始终计算它负责处理的桶。 最后,DBTUX将开始监控有序索引。

STTOR Phase 8

然后我们转到STTOR阶段8.这里首先要运行NDB_STTOR的第7阶段,其中DBDICT启用外键。 如果我们正在进行集群启动/重启,下一个NDBCNTR也将等待所有节点到达此处。 下一个CMVMI将状态设置为STARTED,QMGR将启用与所有API节点的通信。

STTOR Phase 101

在此阶段之后,唯一剩下的阶段是STTOR阶段101,其中SUMA接管它负责异步复制处理的桶的责任。

目前为止主要的潜在消费者:
内存分配中的所有步骤(READ_CONFIG_REQ的所有步骤)。 CMVMI STTOR第1阶段可以锁定内存。 运行节点包含协议的QMGR阶段1。

NDBCNTR STTOR阶段2等待CNTR_START_REQ,DBLQH REDO日志初始化为STTOR阶段2中发生的初始启动类型。鉴于每次只有一个节点可以处于此阶段,这可能会被另一个节点的本地检查点等待停顿 开始。 所以这等待可能相当长。

DBLQH建立与DBACC和DBTUP的连接,这是NDB_STTOR阶段2.NDB_STTOR阶段2中的DBDIH也可以等待元数据被锁定,它可以等待对START_PERMREQ的响应。

对于初始启动,等待DBLQH完成NDB_STTOR阶段3,在此阶段初始化REDO日志的设置。 完成NDB_STTOR阶段3后,在STTOR阶段4中用于集群启动/重启的NDBCNTR必须等待所有节点到达此点,然后它必须等待NDB_STARTREQ完成。

对于节点重启,我们在等待对START_MEREQ信号和START_COPYREQ的响应时有延迟,这实际上是重启的大部分实际工作完成的地方。 重新订阅订阅的SUMA STTOR第5阶段是另一个潜在的时间消费者。

所有等待点都是潜在的时间消费者。 这些主要位于NDBCNTR(等待点5.2,5,1和6)。

Historical anecdotes:

1)NDB内核运行时环境最初是为AX虚拟机设计的。在AX中,开始使用模块MISSRA来驱动各种启动阶段的STTOR / STTORRY信号。 MISSRA后来被并入NDBCNTR,现在是NDBCNTR的子模块。 STTOR和STTORRY的名称在早期的AX系统命名信号方式中有一些基础,但现在已经被遗忘了。 ST至少可以通过启动/重启来完成任务。

2)引入NDB_STTOR的原因是我们设想了一个系统,其中NDB内核只是运行时环境中的一个子系统。因此,我们为NDB子系统引入了单独的启动阶段。随着时间的推移,对这种子系统启动阶段的需求不再存在,但软件已经为此设计,因此它以这种方式保存。

3)数据库启动的分布式部分的责任也是分开的。 QMGR负责发现节点何时上下。 NDBCNTR维护用于故障处理和节点配置的其他更改的协议。最后,DBDIH负责数据库部分的分布式启动。它与DBLQH交互很多,DBLQH负责按照DBDIH的指示启动一个节点数据库部分。

Local checkpoint processing in MySQL Cluster

此注释试图描述MySQL Cluster中发生的检查点处理。 它还阐明了潜在的瓶颈所在。 此注释主要用作MySQL Cluster开源代码的内部文档。

MySQL Cluster中本地检查点的原因是为了确保我们在磁盘上有数据副本,可用于运行REDO日志以在崩溃后恢复MySQL Cluster中的数据。

我们首先在MySQL Cluster中引入不同的重启变体。第一个变体是正常节点重启,这意味着节点已经短时间丢失,但现在又重新上线。我们首先安装所有表的检查点版本(包括执行REDO日志的正确部分)。下一步是使用仍在线的副本使检查点版本保持最新。副本始终按节点组进行组织,节点组的最常见大小是两个节点。因此,当节点启动时,它使用同一节点组中的另一个节点来使在线版本的表重新联机。在正常的节点重启中,我们首先恢复了所有表的稍微旧版本,然后再使用其他节点进行同步。这意味着我们只需要发送自节点重启之前节点失败以来已更新的最新版本的行。我们还有初始节点重启的情况,其中所有数据都必须从另一个节点恢复,因为起始节点中的检查点太旧而无法重用,或者当一个全新的节点启动时它根本不存在。

重新启动的第三个变体是所谓的系统重启,这意味着整个群集在群集崩溃后或在群集受控停止后启动。 在此重新启动类型中,我们首先在运行REDO日志之前在所有节点上恢复检查点,以使系统处于一致且最新的状态。 如果任何节点还原到较旧的全局检查点而不是重新启动的节点,则必须使用节点重新启动中使用的相同代码将这些节点置于联机状态。

系统重启将恢复所谓的全局检查点。 一组事务被组合在一起成为一个全局检查点,当这个全局检查点完成后,属于它的事务是安全的并且将在集群崩溃后继续存在。 我们在第二级运行全局检查点,本地检查点将整个数据集写入磁盘,并且是一个耗时至少几分钟的较长过程。

在可以将起始节点声明为完全恢复之前,它必须参与本地检查点。 崩溃节点错过了恢复群集所需的一组REDO日志记录,因此节点未完全恢复,直到它可用于恢复系统重启时拥有的所有数据。

因此,当执行滚动节点重新启动时,群集中的所有节点都重新启动(例如,升级MySQL群集中的软件),一次重启一组节点是有意义的,因为我们只能在一个节点重新启动一组节点。 时间。

这是了解本地检查站需求的一个先决条件。 我们现在转到如何处理本地检查点的描述。

本地检查点是一个分布式进程。 它由名为DBDIH(简称DIH,DIstribution Handler)的软件模块控制。 DIH包含有关每个片段(与分区的同义词)的各种副本放置在何处以及这些副本上的各种数据的所有信息。 DIH将分发信息存储在每个表的一个文件中。 这个文件实际上是两个文件,这是为了确保我们可以仔细编写文件。 我们首先写文件0,当这个完成后,我们写文件1,这样我们就可以在编写表描述时轻松处理任何崩溃。

当本地检查点完成后,DIH立即启动该过程以启动下一个检查点。 自从我们启动新的本地检查点之前启动本地检查点以来,必须至少完成一个全局检查点。

下一个本地检查点的第一步是检查我们是否已准备好运行它。 这是通过将消息TCGETOPSIZEREQ发送到集群中的所有TC来执行的。 这将通过检查TC中收到的所有写入事务的信息来报告生成的REDO日志信息量。 该消息将由主DIH发送。 主服务器的角色被分配给最旧的幸存数据节点,这使得当目前充当主数据节点的数据节点死亡时,可以轻松选择新主服务器。 所有节点都同意进入群集的节点的顺序,因此节点的年龄在群集中的所有节点中都是一致的。

当所有消息都将REDO日志写入大小返回到主DIH时,我们将它与配置变量TimeBetweenLocalCheckpoints进行比较(此变量以大小的对数设置,因此例如25表示我们等待2 ^ 25个单词的REDO日志已创建于 该集群是128 MByte的REDO日志信息)。
当生成足够数量的REDO日志时,我们启动下一个本地检查点,第一步是清除所有TC计数器,这是通过将TC_CLOPSIZEREQ发送到集群中的所有TC来完成的。

下一步是计算保持GCI(这是需要在REDO日志中保留的最早的全局检查点ID)。 这个数字非常重要,因为我们可以向前移动REDO日志的尾部。 如果我们用完REDO日志空间,我们将无法运行任何写入事务,直到我们启动下一个本地检查点,从而向前移动REDO日志尾部。

我们通过检查每个片段需要恢复的GCI来计算这个数字。 我们目前保留两个旧的本地检查点仍然有效,因此我们不会将GCI移回以使每个片段的两个最旧的本地检查点无效。 完成此计算后可恢复的GCI是循环遍历所有片段时发现的最小GCI。

接下来,我们在集群中所有节点的Sysfile中写下此编号和新的本地检查点ID以及其他一些信息。 在系统重新启动时开始恢复群集时,我们首先看到此Sysfile,因此在此文件中使此类信息正确非常重要。

完成此操作后,我们将计算将参与本地检查点的节点(当前执行重启的早期部分的节点不是本地检查点的一部分,显然也不是死节点)。

我们将有关起始本地检查点的信息发送给系统中的所有其他DIH。我们必须始终保持所有其他DIH的最新状态,以确保在主DIH崩溃或在本地检查点过程中停止时也很容易继续本地检查点。每个DIH记录参与本地检查点的节点集。他们还在每个副本记录上设置一个标志,指示本地检查点正在进行中,在每个片段记录上我们还设置了作为此本地检查点一部分的副本数。

现在我们已经完成了本地检查点的准备工作,现在是时候开始实际检查点写入实际数据了。主DIH通过为应检查点的每个片段副本发送LCP_FRAG_ORD来控制此过程。 DIH目前每个节点有2个这样的LCP_FRAG_ORD未完成,排队的2个片段副本。每个LDM线程可以一次处理一个片段副本的写入,并且可以对排队的下一个片段副本有一个请求。扩展此数字非常简单,以便可以并行写入更多的片段副本,并且可以对更多片段副本进行排队。

当片段副本的本地检查点完成时,LCP_FRAG_REP被发送到所有DIH。 当DIH发现表的所有片段副本都已完成本地检查点时,则应该将表描述写入文件系统。 这将记录所有片段副本的有趣的本地检查点信息。 有两件事可以导致这种情况等待。 首先编写和读取整个表描述只能一次发生一次,这主要发生在正在处理本地检查点时正在进行某些节点故障处理的情况。

可以阻止写表描述的第二件事是,目前最多可以并行写入4个表描述。 这可能很容易成为瓶颈,因为每次写入文件可能需要大约50毫秒。 所以这意味着我们目前每秒只能写出大约80个这样的表。 在具有许多表和少量数据的系统中,这可能成为瓶颈。 然而,它应该不是一个困难的瓶颈。

当主DIH已将所有请求发送到检查点所有片段副本时,它将向所有节点发送一个特殊的LCP_FRAG_ORD,指示不再发送任何片段副本。