1、前言java
事務一直以來是一個玄之又玄的東西,很是難以理解。難以理解倒不是由於事務自己有多難,而是事務這個概念被各類刻意包裝,以致於讓人暈頭轉向,摸不着頭腦。例如各類抽象的概念,一致性、持久性、原子性、持久性、讀未提交、讀已提交、可重複讀、序列化,Spring也抽象了事務的傳播屬性,數據庫自己又有各類鎖,因而咱們就暈頭了。今天咱們就來扒一扒事務,看看事務華麗外衣下的本質。本篇博客結合沈詢的分享,加上本身的一些總結。數據庫
2、事務要解的最終問題——「一致性」安全
一致性是一個名詞,其實這裏實際上應該當成一個動詞來理解,多個參與者達成一致,達成了共識,相互之間不扯皮。這樣說可能比較抽象,舉個生活中的例子。併發
一天,老王和老王老婆,同時去銀行取錢,老王查了銀行卡帳號有1w塊錢,因而取了出來,假設老王老婆和老王同時查詢,也看到有1w塊錢,因而點擊取款,最後發現沒錢可取了,因而要扯皮了,這就不一致了。性能
用一句通俗的話來描述事務的一致性:全部事務的參與方,眼所見,即是心所得。上面的例子中,老王老婆看到了卡上有1w,卻無法取,因而就要扯皮了。這就是嚴苛的一致性所定義的內容。.net
下面咱們來具體分析一下上面的例子。線程
首先引出事務單位的概念,事務單元就是完成一個具體的業務的最小單元,上面的例子中包含兩個事務單元。按照正常的時間線,要想不扯皮,應該看到以下執行順序。3d
可是扯皮的事情發生了,兩個事務單元並行了,因而出現以下的執行順序。事務單元二左移,與事務單元一併行。code
上面的場景似曾相識,其實就是和線程安全所描述的內容一摸同樣,《一篇文章看懂Java併發和線程安全》,要想不扯皮,必須讓訪問的共享資源互斥,用Java代碼能夠描述成以下的代碼:blog
public class Consistency { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); lock.lock(); try { int balance = query();// 查詢餘額 if (balance > 0) { drawingOutCash();// 取出現金 } } catch (Exception e) { } finally { lock.unlock(); } } }
要想強一致,全部的事務單元串行着執行,這就是事務隔離級別中的SERIALIZABLE,因而就引出了事務的隔離級別。
3、事務的隔離級別
強一致,必須讓全部事物單元串行執行,這即是隔離級別中的SERIALIZABLE(序列化),可是這樣系統的性能是可想而知的,幾乎不可用,因而須要放寬對鎖的要求,因此出現了其餘的隔離級別。事務的隔離級別是以性能爲由對一致性的破壞,它的出現是爲了破壞一致性,而不是維持一致性。
事務單元與事務單元的關係只有四種:讀讀、讀寫、寫讀、寫寫
SERIALIZABLE(序列化)
要想進一步提高性能,因而出現了讀寫鎖,這裏就出現了兩種隔離級別:REPEATABLE_READ(可重複讀)和READ_COMMITED(讀已提交)
REPEATABLE_READ(可重複讀):
讀鎖不能被升級爲寫鎖,那麼對共享資源的寫,就進不來,這樣「讀讀」是可並行的,這樣會出現幻讀,由於在這個級別,表是不會被看作是共享資源的,因此能夠insert
READ_COMMITED(讀已提交):
讀鎖能夠被升級爲寫鎖,那麼當對共享資源正在讀時,能夠被寫請求升級爲寫鎖,那麼這樣「讀讀」、「讀寫」能夠並行,因而出現了幻讀、不可重複讀等等現象
READ_UNCOMMITTED
只加寫鎖,讀不用申請鎖,這樣「讀讀」、「讀寫」、「寫讀」均可以並行,但「寫寫」還不能並行,因而全部的寫都是串行,因而就有了髒讀、不可重複讀、幻讀等等。
這就是事務隔離級別的真相,事務隔離級別越低,並行度越好,一致性越低。
4、一句話總結
事務的一致性和線程安全所面對的問題如出一轍,要想維持一致性,須要保證兩點:共享變量的可見性、臨界區代碼訪問的順序性。
快樂源於分享。
此博客乃做者原創, 轉載請註明出處