關於資料庫事務和鎖的一些分析

跳躍的鍵盤手發表於2021-08-03

本文將針對以下幾個問題給大家解答:

1)什麼是事務?事務有哪些特性?

2)不同隔離級別的事務,有什麼區別?

3)瞭解一下資料庫鎖:共享鎖,更新鎖,排它鎖

4)資料庫事務和鎖之間有什麼關係?

5)擴充:什麼是分散式事務?有哪些解決方案?

 

事務

通常是指包含了多個資料庫執行操作(select,update,delete,insert)的一個程式執行單元。

事務的四大特性ACID:

1.原子性(Atomicity):事務中所有資料庫操作要麼全部執行成功,要麼全部執行失敗。

2.一致性(Consistency):事務執行前後資料庫狀態保持一致。比如付款操作:A賬戶減少100元,相應的B賬戶增加100元。

3.隔離性(Isolation):事務與事務之間相互隔離,互不影響,保證了事務之間資料處理的獨立性。

4.永續性(Durability):事務一旦提交成功,那麼對資料庫資料的影響必將持久化到資料庫中,不會因為外在環境,比如斷電,伺服器當機等因素而影響對資料庫資料的變化。事務提交成功之後,會首先記錄到資料庫日誌檔案中,即使斷電重啟後,也會繼續讀取日誌完成事務操作。

事務分類:根據事務中涉及的資料庫操作例項數量區分

    本地事務:事務僅針對同一個資料例項進行操作, 例如:.Net中具體實現如SQLtransaction 

    分散式事務:事務中針對多個資料庫例項進行操作。例如:.Net中的DTC分散式事務,具體實現TransactionScope

事務的隔離性,將事務分為不同隔離級別

1.未提交讀(Read UnCommitted) :允許該事務讀取其他事務未提交的資料。   

   存在問題:髒讀

2.已提交讀 (Read Committed):允許該事務讀取其他事務已提交的資料。

   解決了髒讀的問題

   存在問題:不可重複讀,事務中前後讀取資料不一致。

3.可重複讀 (Repeatable Read):同一個查詢,保證該事務讀取前後資料一致。

   解決了不可重複度的問題

   存在問題:幻讀 ,可能讀取到其他事務新增的資料。

4.序列讀(Serializable):要求事務序列化執行,事務只能一個接著一個地執行,不能併發執行。

   解決了幻讀的問題

SQLServer資料庫事務語法:

SET TRANSATION ISOLATION LEVEL READ UNCOMMITTED--設定事務隔離級別
BEGIN TRANSACTION
...
...--事務執行內容
...
COMMIT/ROLLBACK--提交或者回滾

資料庫鎖
1.共享鎖(S)

1)保護讀取的資料,讀取的過程中,其他併發事務不能修改,刪除資料,可以併發讀取。

2)當事務隔離級別低於“可重複讀”時,一旦資料讀取結束,立即釋放共享鎖,和事務是否結束無關。

2.排它鎖(X)

1)保護修改的資料,獲取當前被修改資源的鎖,該資源同一時刻只能被一個排它鎖佔有,不允許其他併發事務操作該資源(讀取和修改)。

2)排它鎖和共享鎖不能相容。當修改資源時,會自動由共享鎖升級為排它鎖,因此必須等待資源釋放共享鎖,才能獲得排它鎖。

3)如果排它鎖存在於事務中,需等事務結束才能釋放鎖。

3.更新鎖(U)

1)更新鎖時介於共享鎖和排它鎖中間的混合。更新鎖通過uplock手動新增,分兩個階段:1.在修改操作之前,通過更新鎖獲取資源物件 ,其他事務執行緒則只能讀取不能操作  2.然後執行修改操作,將更新鎖升級為排它鎖。在修改操作前實現對資源鎖定,避免了死鎖。

2)更新鎖和共享鎖可以共存,因此使用更新鎖比使用排它鎖解決死鎖問題,效能更優。

3)持有更新鎖的資源,允許被其他併發事務select。

4)如果更新存在於事務中,需等事務結束才能釋放更新鎖。

事務與鎖之間的關係

首先我們說事務和資料庫鎖之間沒有必然的聯絡,但是事務的執行時間會影響鎖持有的時間,間接影響了資料執行效率。

1.通常在執行select查詢的時候會持有共享鎖,查詢一旦執行結束,則立即釋放共享鎖。如果在事務中執行查詢,且隔離級別低於可重複讀,即使事務沒有執行結束,也不會影響共享鎖的釋放。

2.更新鎖和排它鎖,通常在語句執行結束後會立即釋放鎖資源。如果在事務中執行相關鎖操作,需等待事務執行結束,才能釋放鎖。

3.update語句天然就會持有排它鎖,即使不在事務中執行update操作也會持有鎖。

4.事務隔離級別未提交讀(Read UnCommitted)等同於select查詢新增with(nolock),允許讀取“髒資料”

語法:

select * from sys.objects with(nolock) where name='sysrscols';

5.事務隔離級別中的未提交讀、已提交讀和可重複讀都是行級別鎖,對條件範圍內的行資料持有鎖。

6.事務如何通過隔離級別處理併發,以“可重複讀”隔離級別為例:

 1)事務A,預設隔離級別“已提交讀”,查詢操作5秒後執行更新操作

BEGIN TRAN
SELECT * FROM [ORDER] WHERE ID='10'
WAITFOR DELAY '00:00:05'
UPDATE [ORDER] SET PRICE=30 WHERE ID='10'
COMMIT TRAN

2)事務B,隔離級別“可重複讀”,前後兩次查詢間隔10秒

SET TRAN ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT * FROM [ORDER] WHERE ID='10'
WAITFOR DELAY '00:00:10'
SELECT * FROM [ORDER] WHERE ID='10'
COMMIT TRAN

3)啟動事務A後,立即啟動事務B,事務具體執行順序如下:

 a)事務A執行查詢,並持有ID=10資料的共享鎖,查詢結束後釋放鎖,並等待5秒

 b)事務B執行查詢,並持有ID=10資料的共享鎖,由於隔離級別是可重複讀,因此查詢結束後也一直持有共享鎖資源,並等待10秒。

 c)事務A5秒等待結束後,執行update申請持有ID=10資料的排它鎖,此時事務B已經持有了共享鎖,由於共享鎖和排它鎖互斥,所以事務A申請排它鎖失敗,繼續等待事務B釋放鎖資源。這裡其實也正是“可重複讀”隔離級別解決“不可能重複”問題的關鍵。

 d)事務B10秒等待結束後,繼續執行下一個select查詢,並申請持有ID=10資料的共享鎖,由於共享鎖可以共存,事務B申請成功並完成查詢,前後查詢資料保持一致。

 e)事務A在事務B執行結束後,立即獲取到ID=10資料的排它鎖,並執行成功,事務A結束釋放鎖資源。

 

擴充:

鎖分類:

  悲觀鎖:使用資料庫鎖機制,犧牲併發效能,保持事務一致性。

  樂觀鎖:不使用資料庫鎖機制,通常可通過操作前後校驗資料的方式,比如欄位中增加一個版本號version,如果更新前後版本號一致,則執行成功 否則返回錯誤提示,也能保證事務的一致性。

分散式事務

本地事務都是操作的本地單資料庫,分散式事務中不同的操作可能涉及多個不同伺服器上的資料庫例項,因此本地事務不再滿足。

解決方案:

1.兩階段提交(2PC)
準備階段:事務協調者詢問事務中的每個資料庫參與者是否都執行事務成功,如果成功則進入下階段。
提交階段:事務協調者通知事務中每個參與者提交執行操作。
目前.Net中分散式事務支援TransactionScope 需啟動MSDTC服務,即事務協調者。
特點:需要事務中涉及的每個參與者反饋成功才能最終提交,滿足事務一致性,但是阻塞較長。

2.事務補償機制(TCC)
該機制將事務中每個操作都註冊對應的確認和補償操作:分為三個階段
1.try階段:主要是對業務系統做檢測和預留
2.confirm階段:做確認提交,一旦try階段成功,則預設confirm成功
3.cancel取消階段:如果步驟執行失敗,執行回滾。

3.本地事務+MQ訊息
將分散式事務分為多個本地事務,不同伺服器上的本地事務通過MQ訊息傳送。MQ分發的內容需要通過中間訊息表進行記錄並分發。滿足事務最終一致性原則。
如:一個分散式事務中包含A,B兩個不同資料庫伺服器上的操作,A操作執行本地事務+訊息表記錄B操作,MQ傳送訊息表資訊,如果B伺服器接收到資訊,返回訊息接收成功。那麼A本地事務就會執行結束並提交。至於B伺服器是否執行成功,A伺服器不再關心。B伺服器接收到訊息之後,會在B本地事務執行,如果執行失敗,則會一直重試,直到執行成功,以保持事務最終一致性。
其中如果A傳送佇列失敗,也會重試傳送。

相關文章