Verteilte Transaktionen für heterogene Datenbanken in MS .NET

Kürzlich wurde ich in einem Interview gefragt, ob ich mit verteilten Transaktionen in dem Sinne arbeite, dass ich solche Datensätze einfügen / aktualisieren muss, vorausgesetzt:

  1. Einzeltransaktion.
  2. Es können verschiedene Datenbanken wie Oracle, MS SQL Server und PostgreSQL sein.
  3. Die Reaktion auf eine CRUD-Operation kann erheblich sein.
  4. Die Insertionssequenz ist nicht wichtig.

Was haben Kollegen mit dieser Frage erreicht? Überprüfen Sie meine Erfahrungen oder erhalten Sie eine fertige Lösung? Eigentlich ist es für mich nicht wichtig, aber was wichtig ist - das Problem in der Theorie der Frage schien mir interessant zu sein, und ich beschloss, einen Artikel darüber zu schreiben, wie dieses Problem gelöst werden könnte.

Bevor ich meine Lösung beschreibe, erinnern wir uns daran, wie eine typische verteilte Transaktion am Beispiel der Microsoft-Plattform implementiert wird.

Option Nummer 1. C ++ - Anwendung und ODBC-Treiber (im Besitz von Microsoft Distributed Transaction Coordinator (MSDTC) für SQL Server) Mit

Microsoft Distributed Transaction Coordinator (MSDTC) können Anwendungen Transaktionen auf zwei oder mehr Instanzen von SQL Server erweitern oder verteilen. Eine verteilte Transaktion funktioniert auch dann, wenn sich zwei Instanzen auf unterschiedlichen Computern befinden.

MSDTC funktioniert nur lokal für Microsoft SQL Server und ist für den Microsoft Azure SQL-Cloud-Datenbankdienst nicht verfügbar.

MSDTC wird vom SQL Server Native Client-Treiber für Open Database Connectivity (ODBC) aufgerufen, wenn Ihr C ++ - Programm eine verteilte Transaktion verwaltet. Der native Client-ODBC-Treiber verfügt über einen XA Open Distributed Transaction Processing (DTP) -kompatiblen Transaktionsmanager. Diese Konformität wird von MSDTC verlangt. In der Regel werden alle Transaktionsverwaltungsbefehle über diesen ODBC-Treiber für den nativen Client gesendet. Die Reihenfolge ist wie folgt:

Eine ODBC-Anwendung für einen nativen C ++ - Client startet eine Transaktion durch Aufrufen von SQLSetConnectAttr mit deaktiviertem Auto-Commit.
Die Anwendung aktualisiert einige Daten auf SQL Server X auf Computer A.
Die Anwendung aktualisiert einige Daten auf SQL Server X auf Computer B.
Wenn das Update unter SQL Server Y fehlschlägt, werden alle nicht festgeschriebenen Updates in beiden Instanzen von SQL Server zurückgesetzt.
Schließlich schließt die Anwendung die Transaktion ab, indem sie SQLEndTran (1) mit der Option SQL_COMMIT oder SQL_ROLLBACK aufruft.

(1) MSDTC kann ohne ODBC aufgerufen werden. In diesem Fall wird MSDTC zum Transaktionsmanager, und die Anwendung verwendet SQLEndTran nicht mehr.

Nur eine verteilte Transaktion.

Angenommen, Ihre ODBC-Anwendung für einen nativen C ++ - Client ist in einer verteilten Transaktion registriert. Anschließend wird die Anwendung der zweiten verteilten Transaktion gutgeschrieben. In diesem Fall verlässt der SQL Server Native Client-ODBC-Treiber die ursprüngliche verteilte Transaktion und ist in der neuen verteilten Transaktion enthalten.

Lesen Sie hier mehr über MSDTC ..

Option Nummer 2. Die C # -Anwendung wird als Alternative zur SQL-Datenbank in der Azur-Cloud

MSDTC weder für die Azure SQL-Datenbank noch für das Azure SQL Data Warehouse unterstützt.

Sie können jedoch eine verteilte Transaktion für eine SQL-Datenbank erstellen, wenn Ihr C # -Programm die .NET System.Transactions.TransactionScope-Klasse verwendet.

Das folgende Beispiel zeigt, wie mit der TransactionScope- Klasse ein Codeblock für die Teilnahme an einer Transaktion definiert wird.

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;
}

Option Nummer 3. C # - und Entity Framework-Kernanwendungen für verteilte Transaktionen.

Wenn der Datenbankanbieter Transaktionen unterstützt, werden standardmäßig alle Änderungen in einem Aufruf von SaveChanges () auf die Transaktion angewendet. Wenn eine der Änderungen nicht durchgeführt wird, wird die Transaktion zurückgesetzt und keine der Änderungen wird auf die Datenbank angewendet. Dies bedeutet, dass SaveChanges () garantiert entweder erfolgreich abgeschlossen wird oder die Datenbank im Fehlerfall unverändert lässt.

Für die meisten Anwendungen ist dieses Standardverhalten ausreichend. Sie müssen Transaktionen nur dann manuell steuern, wenn die Anforderungen Ihrer Anwendung dies für erforderlich halten.

Sie können die DbContext.Database-API verwendenum Transaktionen zu starten, festzuschreiben und zurückzusetzen. Das folgende Beispiel zeigt zwei SaveChanges () -Operationen und eine LINQ- Abfrage, die in einer einzelnen Transaktion ausgeführt wird.

Nicht alle Datenbankanbieter unterstützen Transaktionen. Einige Anbieter geben möglicherweise Transaktionen aus, wenn sie eine Transaktions-API aufrufen.

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)
        {
             
        }
    }
}

Wenn Sie EF Core in .NET Framework verwenden , unterstützt die Implementierung von System.Transactions dort verteilte Transaktionen.

Wie Sie der Dokumentation im Rahmen der Plattform und der Microsoft-Produktlinie entnehmen können, sind verteilte Transaktionen kein Problem: MSDTC, die Cloud selbst ist fehlertolerant, eine Reihe von ADO .NET + EF Core für exotische Kombinationen (lesen Sie hier ).

Wie kann uns die Transaktionsüberprüfung auf der Microsoft-Plattform helfen, das Problem zu lösen? Ganz einfach, es besteht ein klares Verständnis dafür, dass es einfach kein Standard-Toolkit zum Implementieren einer verteilten Transaktion über heterogene Datenbanken in einer .NET-Anwendung gibt.

Und wenn ja, dann stellen wir unseren eigenen koordinierten Koordinator für verteilte Transaktionen (CRT) her.

Eine der Implementierungsoptionen sind verteilte Transaktionen, die auf Microservices basieren (siehe hier ). Diese Option ist nicht schlecht, erfordert jedoch eine gründliche Verfeinerung der Web-API für alle an der Transaktion beteiligten Systeme.

Deshalb brauchen wir einen etwas anderen Mechanismus. Das Folgende ist ein allgemeiner Ansatz zur Entwicklung von MCT.



Wie Sie dem Diagramm entnehmen können, besteht die Hauptidee darin, den in der Stammdatenbank (oder Tabelle) gespeicherten Transaktionsdatensatz mit dem aktualisierten Status zu erstellen und zu speichern. Mit diesem CRT-Modell können Sie verteilte Transaktionen implementieren, die die folgenden Anforderungen erfüllen:

  • Atomizität
  • Kohärenz
  • Isolation
  • Langlebigkeit

- kann Teil der Anwendung sein, in der der Benutzer einen Datensatz erstellt hat, oder kann eine vollständig separate Anwendung in Form eines Systemdienstes sein. kann einen speziellen Unterprozess enthalten, der Transaktionen in einem Pool zur Ausführung generiert, wenn ein Stromausfall auftritt (im Diagramm nicht dargestellt). Geschäftsregeln zum Konvertieren (Zuordnen) der vom Benutzer generierten Quelldaten für verwandte Datenbanken können dynamisch über das XML / JSON-Format hinzugefügt und konfiguriert und im lokalen Anwendungsordner oder in einem Transaktionsdatensatz (optional) gespeichert werden. Dementsprechend ist es für diesen Fall ratsam, die Konvertierung in verwandte Datenbanken auf Codeebene zu vereinheitlichen, indem die Modularität der Konverter in Form von DLLs implementiert wird. (Und ja, SRT impliziert den direkten Zugriff auf die Datenbank ohne die Teilnahme der Web-API.)

Somit kann SRT in einer einfachen Form erfolgreich als Teil der Anwendung oder als separate, anpassbare und unabhängige Lösung (was besser ist) implementiert werden.

Ich werde noch einmal klarstellen, dass eine verteilte Transaktion per Definition eine einmalige Speicherung von Informationen ist, ohne diese zu ändern, aber mit der Fähigkeit, verschiedene Datenbanken in Datenschemata abzubilden. Wenn Sie also Daten auf anderen Grundlagen wie dem Innenministerium, dem FSB und den Versicherungsunternehmen aufzeichnen müssen, wenn Sie Vorfälle in der Notaufnahme des Krankenhauses (Schusswunde) aufzeichnen, hilft Ihnen dieser Ansatz sicherlich. Der gleiche Mechanismus kann in Finanzinstituten perfekt funktionieren.

Ich hoffe, dieser Ansatz schien Ihnen interessant, schreiben Sie, was Sie darüber denken, Kollegen und Freunde!

All Articles