MS .NET中异构数据库的分布式事务

最近,在一次采访中,有人问我是否从事分布式交易,因为我必须进行此类记录的插入/更新,条件是:

  1. 单笔交易。
  2. 它可以是几个不同的数据库,例如Oracle,MS SQL Server和PostgreSQL。
  3. 对CRUD操作的响应可能很重要。
  4. 插入顺序并不重要。

同事问这个问题有什么收获?查看我的经验或获取现成的解决方案?实际上,这对我来说并不重要,但重要的是-问题理论上的问题对我来说似乎很有趣,因此我决定写一篇文章,介绍如何解决该问题。

在描述解决方案之前,让我们回顾一下如何使用Microsoft平台作为示例来实现典型的分布式事务。

选件编号1。 C ++应用程序和ODBC驱动程序(由SQL Server的

Microsoft分布式事务处理协调器(MSDTC)拥有 Microsoft分布式事务处理协调器(MSDTC)允许应用程序在两个或多个SQL Server实例之间扩展或分发事务。即使两个实例位于不同的计算机上,分布式事务也可以工作。

MSDTC仅在Microsoft SQL Server本地工作,不适用于Microsoft Azure SQL云数据库服务。

当您的C ++程序管理分布式事务时,用于开放式数据库连接(ODBC)的SQL Server本机客户端驱动程序将调用MSDTC。本机客户端ODBC驱动程序具有符合XA开放分布式事务处理(DTP)的事务管理器。 MSDTC需要此合规性。通常,所有事务管理命令都是通过此ODBC驱动程序发送给本地客户端的。顺序如下:

用于本机C ++客户端的ODBC应用程序通过调用禁用了自动提交的SQLSetConnectAttr来启动事务。
该应用程序更新计算机A上SQL Server X
上的某些数据应用程序更新计算机B 上SQL Server X 上的某些数据
如果更新在SQL Server Y上失败,则将回滚两个SQL Server实例中所有未提交的更新。
最后,应用程序通过使用SQL_COMMIT或SQL_ROLLBACK选项调用SQLEndTran(1)来完成事务。

(1)可以在不使用ODBC的情况下调用MSDTC。在这种情况下,MSDTC成为事务管理器,并且该应用程序不再使用SQLEndTran。

仅一项分布式事务。

假设您的本机C ++客户端的ODBC应用程序已注册在分布式事务中。然后,将应用程序记入第二个分布式事务中。在这种情况下,SQL Server Native Client ODBC驱动程序将离开原始的分布式事务,并包含在新的分布式事务中。在此处

阅读有关MSDTC的更多信息

选件编号2。 C#应用程序作为Azur云中SQL数据库的替代方案,

Azure SQL数据库或Azure SQL数据仓库均不支持MSDTC。

但是,如果您的C#程序使用.NET System.Transactions.TransactionScope类,则可以为SQL数据库创建分布式事务。

下面的示例演示如何使用TransactionScope定义代码块以参与事务。

static public int CreateTransactionScope(
    string connectString1, string connectString2,
    string commandText1, string commandText2)
{
    //        StringWriter   
   // .
    int returnValue = 0;
    System.IO.StringWriter writer = new System.IO.StringWriter();

    try
    {
        //  TransactionScope   , 
        //            
        //  .
        using (TransactionScope scope = new TransactionScope())
        {
            using (SqlConnection connection1 = new SqlConnection(connectString1))
            {
                //      
                // TransactionScope   .
                connection1.Open();

                //   SqlCommand    .
                SqlCommand command1 = new SqlCommand(commandText1, connection1);
                returnValue = command1.ExecuteNonQuery();
                writer.WriteLine("   command1: {0}", returnValue);

                //      ,  ,  
                //  command1  .  
                //    using  connection2   connection1, 
                //         connection2 
                //      .   
                using (SqlConnection connection2 = new SqlConnection(connectString2))
                {
                    //       
                    //   connection2 .
                    connection2.Open();

                    //      .
                    returnValue = 0;
                    SqlCommand command2 = new SqlCommand(commandText2, connection2);
                    returnValue = command2.ExecuteNonQuery();
                    writer.WriteLine("    command2: {0}", returnValue);
                }
            } 
            scope.Complete();
        }
    }
    catch (TransactionAbortedException ex)
    {
        writer.WriteLine("   : {0}", ex.Message);
    }

    Console.WriteLine(writer.ToString());

    return returnValue;
}

选件编号3。 C#和Entity Framework Core应用程序用于分布式事务。

默认情况下,如果数据库提供程序支持事务,则一次调用SaveChanges()的所有更改都将应用于事务。如果未执行任何更改,则事务将回滚,并且所有更改均不会应用到数据库。这意味着可以确保SaveChanges()成功完成或在发生错误的情况下保持数据库不变。

对于大多数应用程序,此默认行为就足够了。仅当您的应用程序需求认为必要时,才必须手动控制事务。

您可以使用DbContext.Database API开始,提交和回滚事务。下面的示例显示在单个事务中执行的两个SaveChanges()操作和一个LINQ查询

并非所有的数据库提供程序都支持事务。在调用事务API时,某些提供程序可能会或可能不会发出事务。

using (var context = new BloggingContext())
{
    using (var transaction = context.Database.BeginTransaction())
    {
        try
        {
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();

            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
            context.SaveChanges();

            var blogs = context.Blogs
                .OrderBy(b => b.Url)
                .ToList();

           transaction.Commit();
        }
        catch (Exception)
        {
             
        }
    }
}

如果在.NET Framework中使用EF Core,则System.Transactions的实现将支持分布式事务。

从平台和Microsoft产品线的框架内的文档中可以看到,分布式事务不是问题:MSDTC,云本身是容错的,一堆ADO .NET + EF Core用于奇异的组合(在此处阅读)。

Microsoft平台上的交易审查如何帮助我们解决问题?非常简单,有一个清晰的认识,那就是根本没有标准的工具包可以在.NET应用程序中实现跨异构数据库的分布式事务。

如果是这样,那么我们将创建自己的自制的分布式分布式事务处理协调器(CRT)。

实现选项之一是基于微服务的分布式事务(请参阅此处)。这个选项不错,但是它需要为事务中涉及的所有系统认真完善Web API。

因此,我们需要一个稍微不同的机制。以下是开发MCT的一般方法。



从图中可以看到,主要思想是创建并以更新后的状态存储存储在主数据库(或表)中的交易记录。这种CRT模型允许您实现满足以下要求的分布式事务:

  • 原子性
  • 连贯性
  • 隔离
  • 长寿

-可以是用户在其中创建记录的应用程序的一部分,也可以是系统服务形式的完全独立的应用程序。 可能包含一个特殊的子进程,该子进程会在池中生成事务,以在发生断电时执行(图中未显示)。可以通过XML / JSON格式动态添加和配置用户转换(映射)用户为相关数据库生成的源数据的业务规则,并将其存储在本地应用程序文件夹或事务记录中(作为选择)。因此,对于这种情况,建议通过以DLL形式实现转换器的模块化,以代码级统一对相关数据库的转换。 (是的,SRT意味着无需Web API的参与即可直接访问数据库。)

因此,简单形式的SRT可以作为应用程序的一部分成功实现,也可以作为单独的,可定制的独立解决方案(更好)实现。

再次,我将澄清一下,根据定义,分布式事务是一次性存储信息,而不更改它,但是具有将各种数据库映射到数据方案的能力。因此,如果您需要在内政部,FSB和保险公司等其他机构记录数据,则在医院急诊室(子弹伤)记录事件时,这种方法肯定会对您有所帮助。同样的机制可以在金融机构中完美地发挥作用。

我希望这种方法对您来说似乎很有趣,请写下您对它的看法,同事和朋友!

All Articles