數據庫事務的ACID及三種併發問題

數據庫中事務的四大特性

若是一個數據庫聲稱支持事務的操做,那麼該數據庫必需要具有如下四個特性:spring

一、 原子性(Atomicity)

原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾,這和前面兩篇博客介紹事務的功能是同樣的概念,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。數據庫

二、一致性(Consistency)

一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。多線程

拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。併發

三、隔離性(Isolation)

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

即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始以前就已經結束,要麼在T1結束以後纔開始,這樣每一個事務都感受不到有其餘事務在併發地執行。spa

關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。.net

四、持久性(Durability)

持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。線程

例如咱們在使用JDBC操做數據庫時,在提交事務方法後,提示用戶事務操做完成,當咱們程序執行完成直到看到提示後,就能夠認定事務以及正確提交,即便這時候數據庫出現了問題,也必需要將咱們的事務徹底執行完成,不然就會形成咱們看到提示事務處理完畢,可是數據庫由於故障而沒有執行事務的重大錯誤。事務

數據庫併發事務問題

多個線程開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性。若是不考慮隔離性,會發生以下問題:ip

一、髒讀

一個事務讀取另一個事務還沒有提交的數據。以下圖,線程thread1在事務中在time1時刻向庫表中新增一條數據‘test’並在time3時刻回滾數據;線程thread2在time2時刻讀取,若thread2讀取到‘test’,則爲髒讀。

image

二、不可重複讀

不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內屢次查詢卻返回了不一樣的數據值,這是因爲在查詢間隔,被另外一個事務修改並提交了。

以下圖,線程thread1在事務中time1時刻將數據庫中‘test’更新爲‘00’,並在time3時刻提交;thread2在一個事務中分別在time2和time4兩個時刻讀取這條記錄,若兩次讀取結果不一樣則爲不可重讀。(注意:1.不可重讀針對已經提交的數據。2.兩次或屢次讀取同一條數據。

image

不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另外一個事務未提交的髒數據,而不可重複讀則是讀取了前一事務提交的數據。

在某些狀況下,不可重複讀並非問題,好比咱們屢次查詢某個數據固然以最後查詢獲得的結果爲主。但在另外一些狀況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不一樣,A和B就可能打起來了……

三、幻讀

幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中全部的行的某個數據項作了從「1」修改成「2」的操做,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值仍是爲「1」而且提交給數據庫。而操做事務T1的用戶若是再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺同樣,這就是發生了幻讀。

再舉一個例子,以下圖,線程thread1在事務中time1時刻向數據庫中新增‘00’,並在time3時刻提交;thread2在一個事務中分別在time2和time4兩個時刻掃描庫表,若兩次讀取結果不一樣則爲幻讀。(注意:1.幻讀針對已經提交的數據。2.兩次或屢次讀取不一樣行數據,數量上新增或減小。

image

幻讀和不可重複讀都是讀取了另外一條已經提交的事務(這點就髒讀不一樣),所不一樣的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據總體(好比數據的個數)

 

針對以上問題,數據庫爲咱們提供的四種隔離級別:

① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

③ Read committed (讀已提交):可避免髒讀的發生。

④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。

以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,固然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(相似於Java多線程中的鎖)使得其餘的線程只能在鎖外等待,因此平時選用何種隔離級別應該根據實際狀況。在MySQL數據庫中,支持上面四種隔離級別,默認的爲Repeatable read (可重複讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的爲Read committed級別。

引伸問題:

數據庫自己有事務隔離級別。Spring自己也有事務隔離級別,這兩個設置的優先級是什麼呢?

舉個例子,對於一個spring+oracle的應用來講事務的隔離級別最終是誰來決定的呢?

解答:應用和數據庫之間經過JDBC進行交互,數據庫的事務隔離級別是一個全局的配置,而每一個jdbc鏈接能夠設置本身的事務隔離級別,若是不顯式的設置,則將採用數據庫的事務隔離級別。這樣就不難理解spring爲何也有本身的事務隔離級別了---用於設置每一個jdbc鏈接的事務隔離級別。

相關文章
相關標籤/搜索