數據庫事務的四大特性與隔離級別及測試

四大特性

⑴ 原子性(Atomicity)

  原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

⑵ 一致性(Consistency)

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

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

⑶ 隔離性(Isolation)

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

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

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

⑷ 持久性(Durability)

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

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


隔離級別

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

1,髒讀

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

例:
創建一個表

INSERT INTO `kedb`.`test_table`
(`id`,
`age`)
VALUES
(<{id: }>,
<{age: }>);

插入一個數據:

INSERT INTO `kedb`.`test_table`
(
`age`)
VALUES
(
5);

這裏寫圖片描述

數據庫隔離級別操作:

-- 獲取全局隔離級別  
SELECT @@global.tx_isolation;  
--獲取會話隔離級別  
SELECT @@tx_isolation; 

--全局隔離級別設置  
SET GLOBAL TRANSACTION ISOLATION LEVEL [  
           READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE];  
--會話隔離級別設置  
SET SESSION TRANSACTION ISOLATION LEVEL [  
            READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE];  

查看數據庫的隔離級別

select @@global.tx_isolation;

這裏寫圖片描述

修改級別爲read uncommitted;

set gloabal transaction isolation level read uncommitted;

再查看:

select @@global.tx_isolation;

這裏寫圖片描述

這裏寫圖片描述

在終端A開啓一個修改事務:
這裏寫圖片描述
但是還未提交

這時開啓另一個終端B,查詢:
這裏寫圖片描述

終端A進行回滾事務:
這裏寫圖片描述

現在查詢:
這裏寫圖片描述

(B可以認爲是客戶端,A可認爲是服務端)
可以發現A的事務還未提交時,B就去獲取了,B獲取的是個當前狀態的,但B並不知道A的事務是否已經提交。這樣如果A事務進行回滾數據就不會發生改變,而B卻得到了一個改變後的數據,這就是髒讀。

那麼怎麼解決這一問題?
只要將隔離級別設爲read uncommitted更高的級別就可以了
這裏寫圖片描述
現在再次模擬:
這裏寫圖片描述
可以發現B這時就不會再因爲A事務沒提交而出現髒讀了。

2,不可重複讀

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

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

再舉個例子:
事務1:查詢有雙人牀房間。99號房間,有雙人牀。
事務2:將99號房間,改成單人牀房間。
事務1:再次執行查詢,請求所有雙人牀房間列表,99號房間不再列表中了。也就是說,事務1,可以看到其他事務所做的修改。
在不可重複讀,裏面,可以看到其他事務所做的修改,而導致2次的查詢結果不再一樣了。這裏的修改,是提交過的。也可以是沒有提交的,這種情況同時也是髒讀。
如果,數據庫系統的隔離級別。允許,不可重複讀。那麼你啓動一個事務,並做一個select查詢操作。查詢到的數據,就有可能,和你第2次,3次…n次,查詢到的數據不一樣。一般情況下,你只會做一次,select查詢,並以這一次的查詢數據,作爲後續計算的基礎。因爲允許出現,不可重複讀。那麼任何時候,查詢到的數據,都有可能被其他事務更新,查詢的結果將是不確定的。

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

模擬:
再次之前已經將級別設爲read committed;
這裏寫圖片描述
從以上可以發現事務A在修改時,這時B開始讀取,讀取的是A修改之前的數據。沒有發生髒讀。

現在A提交事務,B再次查詢:
這裏寫圖片描述
這時B查詢到的結果就不是第一次查詢的結果了,所以出現了不可重複讀。

可能你會覺得這樣也很符合邏輯呀,注意這是異步執行的,而且A,B同時執行,可B卻讀取了不同的數據,主要原因就是異步。如果改成同步,A事務再修改的時候,B只能進行等待,當A事務結束後B再執行,這樣B就能得到正確的數據了。

那麼怎麼解決這問題?
可以想象就是同步了,在A執行這個事務的時候,B事務就必須進入等待,在A事務執行完後B再進行執行。
以上是分析,在InnoDB中可以設置隔離級別爲repeatable read解決不可重複讀的問題。

repeatable read
讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
避免了不可重複讀取和髒讀,但是有時可能出現幻讀。這可以通過“共享讀鎖”和“排他寫鎖”實現。

3,虛讀(幻讀)

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

這是最高的隔離級別,它通過強制事務排序使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。只要操作產生了鎖,就不允許其他事務讀取和修改!

在MySQL中,實現了這四種隔離級別,分別有可能產生問題如下所示:
這裏寫圖片描述

REPEATABLE READ 是MySQL的默認事務隔離級別

REPEATABLE READ 這是MySQL的默認事務隔離級別,能確保事務在併發讀取數據時會看到同樣的數據行,解決了READ-COMMITTED隔離級別下的不可重複讀問題。mysql的InnoDB存儲引擎通過多版本併發控制(Multi_Version Concurrency Control, MVCC)機制來解決該問題。在該機制下,事務每開啓一個實例,都會分配一個版本號給它,如果讀取的數據行正在被其它事務執行DELETE或UPDATE操作(即該行上有排他鎖),這時該事物的讀取操作不會等待行上的鎖釋放,而是根據版本號去讀取行的快照數據(記錄在undo log中),這樣,事務中的查詢操作返回的都是同一版本下的數據,解決了不可重複讀問題。其原理如下圖所示:

這裏寫圖片描述

 缺陷:雖然該隔離級別下解決了不可重複讀問題,但理論上會導致另一個問題:幻讀(Phantom Read)。正如上面所講,一個事務在執行過程中,另一個事物對已有數據行的更改,MVCC機制可保障該事物讀取到的原有數據行的內容相同,但並不能阻止另一個事務插入新的數據行,這就會導致該事物中憑空多出數據行,像出現了幻讀一樣,這便是幻讀問題。

而解決幻讀問題,就得使用serializable級別了。
SERIALIZABLE 這是事務的最高隔離級別,通過強制事務排序,使之不可能相互衝突,就是在每個讀的數據行加上共享鎖來實現。在該隔離級別下,可以解決前面出現的髒讀、不可重複讀和幻讀問題,但也會導致大量的超時和鎖競爭現象,一般不推薦使用。


參考:http://www.cnblogs.com/JohnABC/p/3521061.html

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