什么是事務(Transaction)?事務的四個特性以及事務的隔離級別
什么是事務?
- 事務是指作為單個邏輯工作單元執行的一系列操作,要么完全地執行,要么完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。通過將一組相關操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復並使應用程序更加可靠。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
事物的四個特性(ACID)
- 原子性(Atomicity):事務開始后所有操作,要么全部做完,要么全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。
- 一致性(Consistency):事務開始前和結束后,數據庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。
- 隔離性(Isolation):同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。
- 持久性(Durability):事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。
當單進程執行時,很容易保證事務的4中基本要素,並且不會引發問題,然而當多個並發進程共同執行事務時候,可能會引發不同的問題。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
事務的作用
事務管理對於企業級應用而言至關重要,它保證了用戶的每一次操作都是可靠的,即便出現了異常的訪問情況,也不至於破壞后台數據的完整性。就像銀行的自動提款機ATM,通常ATM都可以正常為客戶服務,但是也難免遇到操作過程中及其突然出故障的情況,此時,事務就必須確保出故障前對賬戶的操作不生效,就像用戶剛才完全沒有使用過ATM機一樣,以保證用戶和銀行的利益都不受損失。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
並發事務導致的問題
在許多事務處理同一個數據時,如果沒有采取有效的隔離機制,那么並發處理數據時,會帶來一些的問題。
-
第一類丟失更新:撤銷一個事務時,把其他事務已提交的更新數據覆蓋。
小明去銀行櫃台存錢,他的賬戶里原來的余額為100元,現在打算存入100元。在他存錢的過程中,銀行年費扣了5元,余額只剩95元。突然他又想着這100元要用來請女朋友看電影吃飯,不打算存了。在他撤回存錢操作后,余額依然為他存錢之前的100元。所以那5塊錢到底扣了誰的?
-
第二類丟失更新:是不可重復讀的特殊情況。如果兩個事物都讀取同一行,然后兩個都進行寫操作,並提交,第一個事物所做的改變就會丟失。
小明和女朋友一起去逛街。女朋友看中了一支口紅,(對,女朋友就是用來表現買買買的)小明大方的掏出了自己的銀行卡,告訴女朋友:親愛的,隨便刷,隨便買,我坐着等你。然后小明就坐在商城座椅上玩手機,等着女朋友。這個時候,程序員的聊天群里有人推薦了一本書,小明一看,哎呀,真是本好書,還是限量發行呢,我一定更要買到。於是小明趕緊找到購買渠道,進行付款操作。而同時,小明的女朋友也在不亦樂乎的買買買,他們同時進行了一筆交易操作,但是這個時候銀行系統出了問題,當他們都付款成功后,卻發現,銀行只扣了小明的買書錢,卻沒有扣去女朋友此時交易的錢。哈哈哈,小明真是太開心了!
-
臟讀:臟讀是指在一個事務處理過程里讀取了另一個未提交的事務中的數據。
比如銀行取錢,事務A開啟事務,此時切換到事務B,事務B開啟事務-->取走100元,此時切換回事務A,事務A讀取的肯定是數據庫里面的原始數據,因為事務B取走了100塊錢,並沒有提交,數據庫里面的賬務余額肯定還是原始余額,這就是臟讀。
-
幻讀也叫虛讀:一個事務執行兩次查詢,第二次結果集包含第一次中沒有或某些行已經被刪除的數據,造成兩次結果不一致,只是另一個事務在這兩次查詢中間插入或刪除了數據造成的。幻讀是事務非獨立執行時發生的一種現象。
比如學生信息,事務A開啟事務-->修改所有學生當天簽到狀況為false,此時切換到事務B,事務B開啟事務-->事務B插入了一條學生數據,此時切換回事務A,事務A提交的時候發現了一條自己沒有修改過的數據,這就是幻讀,就好像發生了幻覺一樣。幻讀出現的前提是並發的事務中有事務發生了插入、刪除操作
-
不可重復讀:是指在一個事務里面讀取了兩次某個數據,讀出來的數據不一致。
以銀行取錢為例,事務A開啟事務-->查出銀行卡余額為1000元,此時切換到事務B事務B開啟事務-->事務B取走100元-->提交,數據庫里面余額變為900元,此時切換回事務A,事務A再查一次查出賬戶余額為900元,這樣對事務A而言,在同一個事務內兩次讀取賬戶余額數據不一致,這就是不可重復讀。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
數據庫事務的隔離級別
事務隔離級別,就是為了解決上面幾種問題而誕生的。為什么要有事務隔離級別,因為事務隔離級別越高,在並發下會產生的問題就越少,但同時付出的性能消耗也將越大,因此很多時候必須在並發性和性能之間做一個權衡。所以設立了幾種事務隔離級別,以便讓不同的項目可以根據自己項目的並發情況選擇合適的事務隔離級別,對於在事務隔離級別之外會產生的並發問題,在代碼中做補償 。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
DEFAULT
- 默認隔離級別,每種數據庫支持的事務隔離級別不一樣,如果Spring配置事務時將isolation設置為這個值的話,那么將使用底層數據庫的默認事務隔離級別。順便說一句,如果使用的MySQL,可以使用"select @@tx_isolation"來查看默認的事務隔離級別
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
Read uncommitted(最低級別,任何情況都無法保證)
- 讀未提交,即能夠讀取到沒有被提交的數據,所以很明顯這個級別的隔離機制無法解決臟讀、不可重復讀、幻讀中的任何一種,因此很少使用
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
Read committed(可避免臟讀的發生)
- 讀已提交,即能夠讀到那些已經提交的數據,自然能夠防止臟讀,但是無法限制不可重復讀和幻讀
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
Repeatable read(可避免臟讀、不可重復讀的發生)
- 重復讀取,即在數據讀出來之后加鎖,類似"select * from XXX for update",明確數據讀取出來就是為了更新用的,所以要加一把鎖,防止別人修改它。REPEATABLE_READ的意思也類似,讀取了一條數據,這個事務不結束,別的事務就不可以改這條記錄,這樣就解決了臟讀、不可重復讀的問題,但是幻讀的問題還是無法解決
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
Serializable(可避免臟讀、不可重復讀、幻讀的發生。)
- 串行化,最高的事務隔離級別,不管多少事務,挨個運行完一個事務的所有子事務之后才可以執行另外一個事務里面的所有子事務,這樣就解決了臟讀、不可重復讀和幻讀的問題了
- 但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
隔離級別 | 臟讀 | 不可重復讀 | 幻 讀 | 特點 |
---|---|---|---|---|
未提交讀(read-uncommitted) | √ | √ | √ | 優點在於並發能力高,適合那些對數據一致性沒有要求而追求高並發的場景,缺點是臟讀 |
讀寫提交(read-committed) | × | √ | √ | 出現不可重復讀 |
可重復讀(repeatable-read) | × | × | √ | mysql的默認事務隔離級別。會出現幻讀 |
串行化(serializable) | × | × | × | 並行性低,一般不用。通常消除幻讀使用數據庫鎖的方式 |
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
事務隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
READ_UNCOMMITTED | 允許 | 允許 | 允許 |
READ_COMMITTED | 禁止 | 允許 | 允許 |
REPEATABLE_READ | 禁止 | 禁止 | 允許 |
SERIALIZABLE | 禁止 | 禁止 | 禁止 |
從上往下,級別越來越高,並發性越來越差,安全性越來越高,反之則反
根據您的實際需求,再參考這張表,最后確定事務隔離級別,應該不再是一件難事了。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
事務隔離級別設置得越高越好,事務隔離級別設置得越高,意味着勢必要花手段去加鎖用以保證事務的正確性,那么效率就要降低,因此實際開發中往往要在效率和並發正確性之間做一個取舍,一般情況下會設置為READ_COMMITED,此時避免了臟讀,並發性也還不錯,之后再通過一些別的手段去解決不可重復讀和幻讀的問題就好了。
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
注意: 設置數據庫的隔離級別一定要是在開啟事務之前
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\)
參考資料:https://www.cnblogs.com/xrq730/p/5087378.html
https://www.cnblogs.com/Kevin-ZhangCG/p/9038371.html