在单阶段和多阶段中提交事务

事务中使用的每个资源由资源管理器(RM)管理,其作由事务管理器(TM)协调。 将资源作为事务中的参与者进行登记的主题讨论了如何在事务中登记资源(或多个资源)。 本主题讨论如何在已登记的资源之间协调事务提交。

当事务结束时,应用程序会请求提交或回滚事务。 事务管理器必须消除类似于以下的风险:有些资源管理器投票决定提交,而有些资源管理器却投票决定回滚事务。

如果事务涉及到多个资源,则必须执行两阶段提交 (2PC)。 两阶段提交协议(准备阶段和提交阶段)可确保在事务结束时,会完全提交或完全回滚对所有资源的所有更改。 然后,所有参与者都会被告知最终结果。 有关两阶段提交协议的详细讨论,请参阅 Jim Gray 的“事务处理:概念和技术(Morgan Kaufmann Series in Data Management Systems)ISBN:1558601902”一书。

还可以通过参与单阶段提交协议来优化事务的性能。 有关更多信息,请参阅使用单阶段提交和可提升的单阶段通知进行优化

如果只想通知交易的结果,并且不想参与投票,则应注册该 TransactionCompleted 活动。

两阶段提交 (2PC)

在第一个事务阶段,事务管理器会查询每个资源,以确定是应提交还是回滚事务。 在第二个事务阶段,事务管理器会通知每个资源其查询的结果,以便它执行任何必要的清理。

若要参与这种事务,资源管理器必须实现 IEnlistmentNotification 接口,该接口提供在 2PC 期间由 TM 作为通知调用的方法。 下面的示例演示了此类实现的示例。

class myEnlistmentClass : IEnlistmentNotification
{
    public void Prepare(PreparingEnlistment preparingEnlistment)
    {
        Console.WriteLine("Prepare notification received");

        //Perform transactional work

        //If work finished correctly, reply prepared
        preparingEnlistment.Prepared();

        // otherwise, do a ForceRollback
        preparingEnlistment.ForceRollback();
    }

    public void Commit(Enlistment enlistment)
    {
        Console.WriteLine("Commit notification received");

        //Do any work necessary when commit notification is received

        //Declare done on the enlistment
        enlistment.Done();
    }

    public void Rollback(Enlistment enlistment)
    {
        Console.WriteLine("Rollback notification received");

        //Do any work necessary when rollback notification is received

        //Declare done on the enlistment
        enlistment.Done();
    }

    public void InDoubt(Enlistment enlistment)
    {
        Console.WriteLine("In doubt notification received");

        //Do any work necessary when in doubt notification is received

        //Declare done on the enlistment
        enlistment.Done();
    }
}
Public Class EnlistmentClass
    Implements IEnlistmentNotification

    Public Sub Prepare(ByVal myPreparingEnlistment As PreparingEnlistment) Implements System.Transactions.IEnlistmentNotification.Prepare
        Console.WriteLine("Prepare notification received")

        'Perform transactional work

        'If work finished correctly, reply with prepared
        myPreparingEnlistment.Prepared()
    End Sub

    Public Sub Commit(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Commit
        Console.WriteLine("Commit notification received")

        'Do any work necessary when commit notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub

    Public Sub Rollback(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Rollback
        Console.WriteLine("Rollback notification received")

        'Do any work necessary when rollback notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub

    Public Sub InDoubt(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.InDoubt
        Console.WriteLine("In doubt notification received")

        'Do any work necessary when in doubt notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub
End Class

准备阶段(阶段 1)

在从应用程序接收到 Commit 请求时,事务管理器会对每个已登记的资源调用 Prepare 方法来开始所有已登记参与者的准备阶段,以便获取事务上每个资源的投票。

实现 IEnlistmentNotification 接口的资源管理器应该首先实现 Prepare(PreparingEnlistment) 方法,如下简单示例所示。

public void Prepare(PreparingEnlistment preparingEnlistment)
{
     Console.WriteLine("Prepare notification received");
     //Perform work

     Console.Write("reply with prepared? [Y|N] ");
     c = Console.ReadKey();
     Console.WriteLine();

     //If work finished correctly, reply with prepared
     if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
     {
          preparingEnlistment.Prepared();
          break;
     }

     // otherwise, do a ForceRollback
     else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
     {
          preparingEnlistment.ForceRollback();
          break;
     }
}

当持久资源管理器接收到此调用时,它应记录事务的恢复信息(可通过检索 RecoveryInformation 属性获取)以及在提交时完成事务所需的任何信息。 不需要在 Prepare 方法中执行此作,因为 RM 可以在工作线程上执行此作。

当 RM 完成其准备工作后,应调用 PreparedForceRollback 方法来投票决定是提交还是回滚。 请注意,该PreparingEnlistment类从Done类继承一个Enlistment方法。 如果在准备阶段对 PreparingEnlistment 回调调用此方法,它会通知 TM 它是一个 Read-Only 登记(即可以读取但无法更新受事务保护的数据的资源管理器),RM 不会从事务管理器收到有关第 2 阶段事务结果的进一步通知。

在所有资源管理器都对 Prepared 投票后,会向应用程序通知事务已成功提交。

提交阶段(第 2 阶段)

在事务的第二个阶段中,如果事务管理器从所有资源管理器收到成功准备确认(所有资源管理器都在第 1 阶段结束时调用 Prepared),则会为每个资源管理器调用 Commit 方法。 这样,资源管理器就可使更改永久固定下来并完成提交。

如果有任何资源管理器报告在第 1 阶段的准备失败,则事务管理器应为每个资源管理器调用 Rollback 方法,并向应用程序指出提交失败。

因此,资源管理器应实现以下方法。

public void Commit (Enlistment enlistment)
{
     // Do any work necessary when commit notification is received

     // Declare done on the enlistment
     enlistment.Done();
}

public void Rollback (Enlistment enlistment)
{
     // Do any work necessary when rollback notification is received

     // Declare done on the enlistment
     enlistment.Done();
}

RM 应执行根据通知类型完成事务所需的任何工作,并通过调用 Done 参数方法 Enlistment 通知 TM 已完成该事务。 可在辅助线程上完成此工作。 请注意,第 2 阶段的通知可能在第 1 阶段调用了 Prepared 方法的同一线程上发生内联。 因此,在调用 Prepared 后,您不应执行任何预计在收到第 2 阶段通知前就可完成的操作(如释放锁定)。

实现 InDoubt

最后,应为易失性资源管理器实现InDoubt方法。 如果事务管理器失去与一个或多个参与者的接触,则调用此方法,因此其状态未知。 如果发生这种情况,则应将相关事宜记录下来,以便可在以后调查是否有任何事务参与者事后处于不一致状态。

public void InDoubt (Enlistment enlistment)
{
     // log this
     enlistment.Done();
}

单阶段提交优化

在运行时,单阶段提交协议更高效,因为所有更新都是在没有任何显式协调的情况下完成的。 有关此协议的更多信息,请参阅使用单阶段提交和可提升的单阶段通知进行优化

另请参阅