Martin
by Martin Kysel

两阶段提交的各种口味 - 解释

内部,NuodB使用两相提交协议来管理用户数据的持久性。 NOODB还支持X / OPEN XA协议,用于同步多个数据存储的全局事务。 XA有时也称为两阶段提交。这两个协议中的基本原则都是相似的,而是提供不同的目的。让我们探索这两个协议之间的差异。

两个资源的单一事务

让我们探索一个简单的用例。简单的应用程序从一个数据源(outgoing_messages)中获取消息,并将它们写入新的数据源(Incoming_messages)。这是一个相当常见的用例,如果您从消息队列中读取消息,例如Apache Activemq,并且将它们写入数据库(例如NOODB)。以下代码片段在相应的SQL表单中显示此示例。


SQL > select * from outgoing_messages;

 ID     MSG    
 --- --------- 

  1  message 1 

SQL > start transaction;
SQL > select * from outgoing_messages;

 ID     MSG    
 --- --------- 

  1  message 1 

SQL > delete from outgoing_messages where id=1;
SQL > insert into incoming_messages(id, msg) values(1, 'message 1');
SQL > commit;

在单个关系数据库中执行时,预计两个语句(插入和删除)将根据 酸保证。他们都成功或他们都失败了。在整个本文中,我们专注于酸的A(Tomic)保证。

XA摘要在不同数据存储中居住的方案的语句和交易生命周期。以下示例是ActiveMQ消费者的简化版本,用于收到消息并将其写入NuodB。由于本文中的空间约束,代码不包含任何设置或故障处理。


​javax.jms.MessageConsumer consumer=xasession.createConsumer(queue);
        MessageListener listener = new MessageListener() {
            @Override
            public void onMessage(Message msg) {
                TextMessage msg1=(TextMessage)msg;

                NuoXADataSource nuodbDs = new NuoXADataSource();
                XAConnection nuodbXAconn = nuodbDs.getXAConnection(DBA_USER, DBA_PASSWORD);

                XAResource mqRes = xasession.getXAResource();
                XAResource nuoRes = nuodbXAconn.getXAResource();

                nuodbStmt.executeUpdate(String.format("insert into incoming_messages(id, msg) values(1, '%s')", msg1.getText()));

                mqRes.end(xid, XAResource.TMSUCCESS);
                nuoRes.end(xid, XAResource.TMSUCCESS);

                mqRes.prepare(xid);
                nuoRes.prepare(xid);

                mqRes.commit(xid, false);
                nuoRes.commit(xid, false);
            }
        };

Nuodb中的内部两阶段提交

当您在NOODB中提交事务时,您必须等待一段时间等于执行事务和最慢的SM之间的TE之间最慢的链接的网络往返。对于诺福德架构的复习, 请阅读本文。 NOODB中的默认提交协议是 安全的,所以每次交易都需要通过所有短信确认。还可以将提交协议更改为等待SMS子集的较弱保证,有效地交易延迟持久性保证。

提交协议包含三条消息。首先,TE通知所有SMS关于提交意图(预先提交)。然后TE等待来自所有SMS的确认(提交ACK)。第三条消息被广播到群集中的所有引擎,通知它们提交成功。由于事务不等待要确认的第三条消息,因此用户永远不必等待它,并且提交协议将控件返回给用户应用程序(COMMING成功)。提交消息到达远程事务引擎后,将在这些引擎上启动的未来事务将看到承诺交易的所有效果。

安全提交保证即使在灾难性的发动机故障面上也会持久。可靠的广播协议保证交易无处不在或无处可行。第三条消息对于耐久性不是必需的,因为NOODB可以在从失败恢复期间推断出事务的正确状态。只要至少一个SM存活灾难性事件,就可以恢复交易。

预先提交消息包含在该引擎的版本向量中提交事务的顺序。如果您不确定版本向量是什么以及为什么Nuodb正在使用它们,请不要犹豫,在评论部分(或等待该主题的专用博客文章)。重要位是订单已经定义,并且所有引擎都会相同。所有事务引擎都将以相同的顺序从相同的交易引擎中查看所有提交。

如果事务引擎因灾难性故障而失败,则该群集协调任何持续的事务并自动完成提交协议。这意味着如果在广播提交消息之前发生事务引擎失败,则将生成替换消息,并且将可见交易。这是我们在以下部分进一步探索的XA的重要对比。

总结:前两条消息用于耐用性。最后一条消息用于使更改在群集中可见。

XA两阶段提交解释

XA使用相同的三条消息来跨多个数据存储跨越事务 - 在我们的情况下是消息队列和NOODB。首先,它发送预先提交,这会通知数据存储对提交的意图。数据存储带有Commit-Ack的答案,并保证它将永远能够在将来提交交易。这意味着,任何后续冲突的事务都必须等到XA事务在执行之前解决。一旦所有数据存储答案,并在提交确认后,可以全局提交事务。如果任何数据存储未能提交 - ACK,则指示所有数据存储都放弃预先提前的事务。

一旦所有数据存储确认预先提交,提交消息就会向所有数据存储广播。当然,这条消息到达不同的时间。由于网络延迟和流程寿命不可预测,消息可能会在任意时到达。还有可能崩溃的数据存储和交易仅在其他商店中部分地提交。一旦崩溃的数据存储备份后,事务管理器需要协调状态。这意味着XA事务管理器需要持续到潜在的长时间状态。

XA中的承诺是有多好的承诺?在确认预先提交后,不允许将数据存储缩回其保证提交更改。在现代应用中,根据此类保证证明有其局限性。磁盘有故障,电缆切割,电动停电可能持续很长时间。虽然大多数数据存储可以在内部保证预先提交将始终能够提交,但没有数据存储可以保证由于任意外部因素导致状态永远不会丢失。因此,XA很难依赖。

如果发生灾难性失败,当XA预先提交丢失并且XA提交永远无法完成时,从其他XA参与者中撤消承诺的事务可能非常困难。撕裂的交易是一个不可见的事务。换句话说,只有事务的某些部分(例如单个语句)成功突变了数据存储状态,而其他部分则没有。在灾难性失败的情况下,XA违反了(统一)保证。

虽然大多数应用程序应考虑到失败,但我们理解撕裂的交易不是规范。因此,让我们探索正常情况而没有任何故障。即使没有失败,XA也永远不会全球原子。让我们回到原来的例子。应用程序从消息队列中读取消息并将其写入Nuodb。根据参与者接收和处理网络数据包的方式,在任何给定时间的两个涉及的参与者中都可能存在于两者中的消息。要使参数更少抽象,让我们想象另一个应用程序同时读取来自两个数据存储的状态。如果Xa是全局酸,那么只有两种可能的结果:消息在消息队列中,但不在诺福德;或者消息不在队列中,但在NOODB中。

孤立XA事务(未观察到一个数据存储中的最终提交消息的状态转换),一致(与其他事务相同,状态转换需要导致数据存储中的有效状态)和持久(一旦提交,除非发生灾难性失败,否则国家不会丢失)。 XA交易不是原子的。

多台机器的单一事务

让我们返回内部Nuodb提交协议。由于NOODB能够从失败中恢复并且可靠的广播协议保证所有消息使其成为所有引擎,因此用户永远不会手动撤消部分提交的事务。所有交易都是Nuodb的酸。不需要一个必须持续态度的事务管理器。

Nuodb的交易永远不会被撕裂。单个事务始终在同一事务引擎上执行。即使事务修改多个资源(表),那些修改也是相同事务的一部分。这对于熟悉NoSQL系统中熟悉两相犯罪的读者来说是一个重要的区别。

概括

如您所见,Nuodb将分布式状态转换从耐用性保证分开,作为两相提交的一部分。前两条消息(预先提交和预先提交--Ack)保证已满足提交协议,并且在失败后可以恢复事务。第三条消息(提交)使得更改在整个群集中原子可见。

虽然类似于Xa,但是Nuodb使用的内部两相协议不会遭受相同的限制。

NOODB支持 X / OPEN XA Transactions 自版本3.0以来。使用XA时意识到固有的XA全局违规行为。

准备尝试自己? 立即下载Nuodb Community Edition。 

 

¹ XA中的事务管理器是用户应用程序,其同步数据存储。