本文將針對以下幾個問題給大家解答:
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發送隊列失敗,也會重試發送。
