談談Java事務


事務具基本特徵(ACID)


Atomi(原子性):事務中包含的操做被看作一個整,要麼徹底部成功,要麼所有失敗。數據庫

② Consistency(一致性):事務在完成時,必須是全部的數據都保持一致狀態,保證了數據的完整性和一致性。併發

③ Isolation(隔離性):當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。.net

這裏的隔離性就是下面咱們要說的隔離級別,爲了減小事務在修改數據上的相互影響。blog

④ Durability(持久性):一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使數據庫系統遇到故障也不會丟失提交事務的操做。事務


事務的傳播行爲


事務傳播行爲(propagation behavior)指的就是當一個事務方法A被另外一個事務方法B調用時,這個事務方法A應該如何進行。換句話說,須要發生方法件的調用,纔會存在傳播行爲,對於單個事務方法而已,沒有傳播的概念。ci

傳播行爲 含義 應用場景
REQUIRED 默認傳播行爲,當前上下文不存在事務,因此會開啓一個新的事務,當上下文存在事務,則融入當前事務(它是個機會主義者,既能夠暗度陳倉,也能夠獨當一面) 大部分簡單場景
SUPPORTS 若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行(它是個保守派,隨波逐流,從不另起爐竈) 查詢
MANDATORY 若是存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常(它是個獨裁者,只許州官放火,不準百姓點燈)
REQUIRES_NEW 它會開啓一個新的事務。若是一個事務已經存在,則先將這個存在的事務掛起。(它是個革新派,開天闢地) 批量操做時,須要對單調數據進行控制
NOT_SUPPORTED 老是非事務地執行,並掛起任何存在的事務(他是個悲觀主義者,不主動,誰來都拒絕)
NEVER 老是非事務地執行,若是存在一個活動事務,則拋出異常(怎麼說,這個有點反人類)
NESTED 嵌套事務一個很是重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所作的動做。而內層事務操做失敗並不會引發外層事務的回滾。(害,活像一個愛情弱勢方,受害者,左右不了對方,被對方左右)

事務的隔離級別


準確來講,事務的隔離級別只有四個,分別爲:資源

  • 讀未提交(READ_UNCOMMITTED)
  • 讀已提交(READ_COMMITTED)
  • 可重複讀(REPEATABLE_READ)
  • 串行化(SERIALIZABLE)

單單看這幾個字有點幹,其餘的先無論,先來看一下他們都能解決啥問題,以及沒法解決什麼問題先get

異常現象 髒讀 不可重複讀 幻讀
未提交讀
讀寫提交 ×
可重複讀 × ×
串行化 × × ×

如何理解上面這個表格呢?首先,若是是簡單的查詢,那是不存在的什麼問題的,由於你只進行了查詢而已。那麼若是你有對數據庫的更新修改操做,是否是就會產生問題呢?不必定,若是這個時間內只有一個事務,那你作再多的操做,都是不會有問題的,要麼這個事務的操做所有成功,要麼所有失敗。因此在對事務產生問題的討論上,都是針對併發事務。這一點與事務的傳播性的特色同樣。簡單的理解,就是同一時間,對同一數據有多個事務在操做。it

那咱們都知道,每個事務的裏的操做,都會被計算機分割成不少的原子操做,由cpu進行調度執行,因此就涉及這些操做的一個排列問題,但咱們知道cpu的調度是隨機的,因此的就會產生不少可能性。這些操做的執行順序,致使了面臨一些問題。io

最低級別讀未提交(READ_UNCOMMITTED),什麼問題都有可能發生,不適合併發事務場景

由上面所提到的事務的持久性能夠得知,事務的持久化,是創建在事務被提交的基礎上的。也就是說,事務A沒提交,對數據的操做都不算真正的生效,那麼若是在未提交前,被別的事務B的讀取到這部分數據,那麼你最終提交的化還好,嚴格來講不算髒數據,但假如事務A回滾了,那讀的這部分數據實際上是錯誤的,咱們把它稱之爲「髒數據」,這是併發事務面臨的第一個問題。

要解決「髒數據」問題,就必須保證一個事務不會讀到另外一個並行事務已修改但未提交的數據,這正是讀已提交(READ_COMMITTED)隔離級別所要求的。換句話說就是「我修改的數據還沒提交你不能讀」。解決了髒數據問題後。我讀到的數據的確都是「生效」的了。

但這會帶來一個問題,單事務A須要對一個數據屢次讀取的時候,中間可能存在一個可能:事務B修改這個數據了,由該級別讀到的數據是已提交可知,事務B對數據的修改操做確定是生效的了,因此事務A屢次讀到的結果可能不一致,這就是「不可重複讀」問題。

要解決「不可重複讀」問題,很顯然,我就得增強約束,上一級是我修改的數據還沒提交你不能讀,此次是當我讀數據開始,到我所在的事務還沒提交以前,你不能讀,這意味着無論事務A讀數據後有多少操做,併發事務B都得等待,能夠看到的「鎖住」的範圍更大了,也相應帶來更大的損耗。此時解決重複讀的問題,級別是可重複讀(REPEATABLE_READ)

但可重複讀級別解決的是同一競爭數據的重複讀問題,假如事務A屢次經過特定條件屢次讀取m條數據,有事務C,插入n條數據服務事務A查詢條件,或者修改了其餘t條數據一樣符合事務A的條件,那事務A後面讀取可能就是m+n或m+t條數據了,此爲」幻讀「現象,要解決此問題

要解決幻讀現象,只能祭出最後殺招,串行化(SERIALIZABLE)級別

總結以下

隔離等級 含義
READ_UNCOMMITTED 最低等級,從上表能夠看到,啥問題解決不了,因此當併發事務操做同一數據時,啥狀況均可能發生,因此通常不用於併發事務場景
READ_COMMITTED 保證了一個事務不會讀到另外一個並行事務已修改但未提交的數據
REPEATABLE_READ 保證了一個事務不會修改已經由另外一個事務讀取但未提交(回滾)的數據
SERIALIZABLE 最嚴格的級別,事務串行執行,資源消耗最大

數據庫實現


未提交讀的數據庫鎖狀況(實現原理)

事務在讀數據的時候並未對數據加鎖。

務在修改數據的時候只對數據增長行級共享鎖

提交讀的數據庫鎖狀況

事務對當前被讀取的數據加 行級共享鎖(當讀到時才加鎖),一旦讀完該行,當即釋放該行級共享鎖

事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。

可重複讀的數據庫鎖狀況

事務在讀取某數據的瞬間(就是開始讀取的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放;

事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。

可序列化的數據庫鎖狀況

事務在讀取數據時,必須先對其加 表級共享鎖 ,直到事務結束才釋放;

事務在更新數據時,必須先對其加 表級排他鎖 ,直到事務結束才釋放。


參考資料


數據庫隔離級別 及 其實現原理

相關文章
相關標籤/搜索