談談事務的四大特性

一、事務四大特性(ACID)

1、 A(Atomicity)原子性

原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾。

因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

2、C(Consistency)一致性

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

拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。

3、I(Isolation)隔離性

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

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

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

4、D(Durability)持久性

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

例如我們在使用JDBC操作數據庫時,在提交事務方法後,提示用戶事務操作完成,當我們程序執行完成直到看到提示後,就可以認定事務以及正確提交,即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因爲故障而沒有執行事務的重大錯誤。

二、事務的隔離性

以上介紹完事務的四大特性(簡稱ACID),現在重點來說明下事務的隔離性,當多個線程都開啓事務操作數據庫中的數據時,數據庫系統要能進行隔離操作,以保證各個線程獲取數據的準確性,我們可以先看看如果不考慮事務的隔離性,會發生的幾種問題:

1、髒讀

髒讀是指在一個事務處理過程裏讀取了另一個未提交的事務中的數據。

當一個事務正在多次修改某個數據,而在這個事務中這多次的修改都還未提交,這時一個併發的事務來訪問該數據,就會造成兩個事務得到的數據不一致。例如:用戶 A 向用戶 B 轉賬 100 元,對應 SQL 命令如下:

update account set money = money + 100 where name = 'B';  (此時 **A** 通知 **B**)

update account set money = money - 100 where name = 'A';

當只執行第一條 SQL L時,A 通知 B 查看賬戶,B 發現確實錢已到賬(此時即發生了髒讀),而之後無論第二條 SQL 是否執行,只要該事務不提交,則所有操作都將回滾,那麼當 B 以後再次查看賬戶時就會發現錢其實並沒有轉。

2、不可重複讀

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

例如: 事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重複讀。

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

在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個數據當然以最後查詢得到的結果爲主。但在另一些情況下就有可能發生問題,例如對於同一個數據 A 和 B 依次查詢就可能不同, A 和 B 就可能打起來了……

3、虛讀(幻讀)

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

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

三、MySQL提供的四種隔離級別

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

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

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

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

級別由高至低爲:① > ② > ③ > ④

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

在MySQL數據庫中查看當前事務的隔離級別:

select @@tx_isolation;

20191104102805.png

在MySQL數據庫中設置事務的隔離 級別:

set tx_isolation= '隔離級別名稱'

20191104103235.png

記住:設置數據庫的隔離級別一定要是在開啓事務之前!

如果是使用 JDBC 對數據庫的事務設置隔離級別的話,也應該是在調用 Connection 對象的 setAutoCommit(false) 方法之前。調用 Connection 對象的 setTransactionIsolation(level) 即可設置當前鏈接的隔離級別,至於參數 level ,可以使用 Connection 對象的字段:

20191104103406.png

在JDBC中設置隔離級別的部分代碼:

20191104103427.png

後記:

隔離級別的設置只對當前鏈接有效。對於使用MySQL命令窗口而言,一個窗口就相當於一個鏈接,當前窗口設置的隔離級別只對當前窗口中的事務有效;對於JDBC操作數據庫來說,一個Connection對象相當於一個鏈接,而對於Connection對象設置的隔離級別只對該Connection對象有效,與其他鏈接Connection對象無關。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章