Transactions-事務

一. 概念

事務是提供一種機制將一個活動涉及的所有操作納入到一個不可分割的執行單元,組成事務的所有操作只有在所有操作均能正常執行的情況下方能提交,只要其中任一操作執行失敗,都將導致整個事務的回滾。

二. 事務的的四大特性,ACID

A:原子性(Atomicity)

事務的原子性指的是,事務中包含的程序作爲數據庫的邏輯工作單位,它所做的對數據改操作要全部執行,要麼全部不執行。這種特性稱爲原子性。 

事務的原子性要求,如果把一個事務看作是一個程序,它要麼完整的被執行,要麼完全執行。就是說事務的操縱序列或者完全應用到數據庫或者完全不影響數據庫。這種特性稱爲原子性  假如用戶在一個事務內完成了對數據庫的更新,這時所有的更新對外部世界必須是可見的,或者完全沒有更新。前者稱事務已提交,後者稱事務撤銷。DBMS必須確保由成功提交的事物完成的所有操作在數據庫內有完全的反映,而失敗的事務對數據庫完全沒有影響

 C:一致性(Consistency)

事務的一致性指的是,在一個事務執行之前和執行之後數據庫都必須處於一致性狀態。這種特性稱爲事務的一致性。假如數據庫的狀態滿足所有的完整性約束,就說該數據庫是一致的。

一致性處理數據庫中對所有語義約束的保護。假如數據庫的狀態滿足所有的完整性約束,就說該數據庫是一致的。例如,當數據庫處於一致性狀態S1時,對數據庫執行一個事務,在事務執行期間假定數據庫的狀態是不一致的,當事務執行結束時,數據庫處在一致性狀態S2

I:隔離性(Isolation)

隔離性指併發的事務是相互隔離的。即一個事務內部的操作及正在操作的數據必須封鎖起來,不被企圖進行修改的事務看到  分離性是DBMS針對併發事務間的衝突提供的安全保證。DBMS可以通過加鎖在併發執行的事務間提供不同級別的分離。假如併發交叉執行的事務沒有任何控制。操縱相同的共享對象的多個併發事務的執行可能引起異常情況。

DBMS可以在併發執行的事務間提供不同級別的分離。分離的級別和併發事務的吞吐量之間存在反比關係。較多事務的可分離性可能會帶來較高的衝突和較多的事務流產。流產的事務要消耗資源,這些資源必須要重新被訪問。因此,確保高分離級別的DBMS需要更多的開銷

D:持久性(Durability)

持久性意味着當系統或介質發生故障時,確保已提交事務的更新不能丟失。即一旦一個事務提交,DBMS保證它對數據庫數據的改變應該是永久性的,耐得住任何系統故障

。持久性通過數據庫備份和恢復來保證  持久性意味着當系統或介質發生故障時,確保已提交事務的更新不能丟失。即對已提交事務的更新恢復。一旦一個事務被提交,DBMS必須保證提供適當的冗餘,使其耐的住系統故障。所以,持久性主要在於DBMS的恢復性能

當然,如果你需要自定義事務的原子性邏輯,就需要了解一下顯式事務和隱式事務。

  • 隱式事務,就是SQL Server爲你自動提交事務,當事務中運行到某一條失敗時,會依次回滾之前的操作。
  • 顯式事務,就是由你自己指定需要哪些工作必須完成的事務,當事務運行中如果指定工作未完成,會觸發回滾操作。

下面演示一下,在NetCore中,事務的幾種用法。

1.簡單事務

using (var context = new BloggingContext())
{
    using (var transaction = context.Database.BeginTransaction())
    {
         //插入一條記錄
        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();
       //提交事務如果所有命令都成功,事務將自動回滾
    }
}

2.跨上下文事務

//首先,我們創建同一連接
var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseSqlServer(new SqlConnection("數據庫連接字符串"))
    .Options;
//創建第一個上下文
using (var context1 = new BloggingContext(options))
{
    //創建事務
    using (var transaction = context1.Database.BeginTransaction())
    {
            //第一個上下文進行業務添加
            context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context1.SaveChanges();
            //創建第二個上下文
            using (var context2 = new BloggingContext(options))
            {
                //使用DbContext.Database.UseTransaction(DbTransaction)API在同一事務中登記兩個上下文
                context2.Database.UseTransaction(transaction.GetDbTransaction());
                //第二個上下文進行業務查詢
                var blogs = context2.Blogs
                    .OrderBy(b => b.Url)
                    .ToList();
            }

            //提交事務
            transaction.Commit();
    }
}          

PS:以上寫法,僅在測試SQLServer2017數據庫時成功組合,使用MySql數據庫則拋出了異常,應該是因爲數據庫特性導致

3.多種數據訪問技術時共享事務

using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
        try
        {
            // 使用ADO.NET訪問數據庫
            var command = connection.CreateCommand();
            command.Transaction = transaction;
            command.CommandText = "DELETE FROM dbo.Blogs";
            command.ExecuteNonQuery();

            // 創建EF Core連接
            var options = new DbContextOptionsBuilder<BloggingContext>()
                .UseSqlServer(connection)
                .Options;
            //創建上下文實例
            using (var context = new BloggingContext(options))
            {
                //添加事務到上下文
                context.Database.UseTransaction(transaction);
                //上下文業務操作
                context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                context.SaveChanges();
            }

            //提交事務
            transaction.Commit();
        }
        catch (System.Exception)
        {
            // TODO: Handle failure
        }
    }
}

4.環境事務

環境事務內,當發生任何異常時,都會觸發事務的回滾。

我們可以通過在創建TransactionScope作用域時,使用IsolationLevel來設定環境事務的隔離性以及超時時間

4.1 TransactionOptions

TimeOut 獲取或設置該事務的超時時間
IsolationLevel 獲取或設置事務的隔離級別

 

 

 

4.2 TransactionScopeOption

Required 該範圍需要一個事務。 如果已經存在環境事務,則使用該環境事務。 否則,在進入範圍之前創建新的事務。 這是默認值
RequiresNew 總是爲該範圍創建新事務。
Suppress 環境事務上下文在創建範圍時被取消。 範圍中的所有操作都在無環境事務上下文的情況下完成。

 

 

 

 

using (var scope = new TransactionScope(
    TransactionScopeOption.Required,
    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        try
        {
            // Run raw ADO.NET command in the transaction
            var command = connection.CreateCommand();
            command.CommandText = "DELETE FROM dbo.Blogs";
            command.ExecuteNonQuery();

            // Run an EF Core command in the transaction
            var options = new DbContextOptionsBuilder<BloggingContext>()
                .UseSqlServer(connection)
                .Options;

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

            // Commit transaction if all commands succeed, transaction will auto-rollback
            // when disposed if either commands fails
            scope.Complete();
        }
        catch (System.Exception)
        {
            // TODO: Handle failure
        }
    }
}

5.顯式事務

using (var transaction = new CommittableTransaction(
    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    var connection = new SqlConnection(connectionString);

    try
    {
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
            context.Database.OpenConnection();
            context.Database.EnlistTransaction(transaction);

            // Run raw ADO.NET command in the transaction
            var command = connection.CreateCommand();
            command.CommandText = "DELETE FROM dbo.Blogs";
            command.ExecuteNonQuery();

            // Run an EF Core command in the transaction
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
            context.Database.CloseConnection();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        transaction.Commit();
    }
    catch (System.Exception)
    {
        // TODO: Handle failure
    }
}

6.嵌套事務

當嵌套類的TransactionScope的TransactionScopeOption爲Required的時候,二者的事務的ID都是同一個,只有當2個TransactionScope都complete的時候才能算真正成功。

如果把TransactionScopeOption設爲RequiresNew,則嵌套的事務塊和外層的事務塊各自獨立,互不影響。

TransactionScopeOption設爲Suppress則爲取消當前區塊的事務,一般很少使用。

對於多個不同服務器之間的數據庫操作,TransactionScope依賴DTC(Distributed Transaction Coordinator)服務完成事務一致性。

static void NestedScopes()
        {
            using (var scope = new TransactionScope())
            {
                Transaction.Current.TransactionCompleted += OnTransactionCompleted;

                Utilities.DisplayTransactionInformation("Ambient TX created",
                      Transaction.Current.TransactionInformation);

                using (var scope2 =
                      new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    Transaction.Current.TransactionCompleted += OnTransactionCompleted;

                    Utilities.DisplayTransactionInformation(
                           "Inner Transaction Scope",
                           Transaction.Current.TransactionInformation);

                    scope2.Complete();
                }
                scope.Complete();
            }

        }

五. 相關資料

5.1 分佈式事務,布魯爾定理,BASE,2PC,TCC,MQ,Saga的資料

5.2 微軟官方文檔

5.3 TransactionScope的使用方法和原理

聲明:本文部分內容摘自上述博客以及文檔。

PS:如果你需要第一時間查閱到我的博客,歡迎訪問我的個人主頁:https://www.magicalconch.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章