數據庫的事務四大特性


原文鏈接:https://www.cnblogs.com/xll1025/p/6429157.html

事務的四大特性(ACID)

原子性(Atomicity)

原子性是指事務是一個不可分割的工作單位,事務中的操作要麼全部成功,要麼全部失敗。比如在同一個事務中的SQL語句,要麼全部執行成功,要麼全部執行失敗。

一致性(Consistency)

官網上事務一致性的概念是:事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。還要一種說法是事務前後數據的完整性必須保持一致。以轉賬爲例子,A向B轉賬,假設轉賬之前這兩個用戶的錢加起來總共是2000,那麼A向B轉賬之後,不管這兩個賬戶怎麼轉,A用戶的錢和B用戶的錢加起來的總額還是2000,這個就是事務的一致性。

隔離性(Isolation)

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

持久性(Durability)

持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。

事務的隔離級別

事務的四大特性中最麻煩的是隔離性,下面重點介紹一下事務的隔離級別。
多個線程開啓各自事務操作數據庫中數據時,數據庫系統要負責隔離操作,以保證各個線程在獲取數據時的準確性。

髒讀

指一個事務讀取了另外一個事務未提交的數據。

//這是非常危險的,假設a向b轉帳100元,對應sql語句如下所示:
update account set money=money+100 while name=‘b’; 
update account set money=money-100 while name=‘a’;

當第1條sql執行完,第2條還沒執行(a未提交時),如果此時b查詢自己的帳戶,就會發現自己多了100元錢。如果a等b走後再回滾,b就會損失100元。

不可重複讀

在一個事務內讀取表中的某一行數據,多次讀取結果不同。(一個事務讀取到了另外一個事務提交的數據)
例如銀行想查詢a帳戶餘額,第一次查詢a帳戶爲200元,此時a向帳戶內存了100元並提交了,銀行接着又進行了一次查詢,此時a帳戶爲300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準的。可將例子簡化爲:讀表中某一行數據,例如a賬戶第一次讀爲1000,第二次讀爲1100。
不可重複讀和髒讀的區別是,
髒讀是讀取前一事務未提交的髒數據,不可重複讀是重新讀取了前一事務已提交的數據。
很多人認爲這種情況就對了,無須困惑,當然是以後面的結果爲準了。我們可以考慮這樣一種情況,比如銀行程序需要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,導致文件和屏幕中的結果不一致,銀行工作人員就不知道以哪個爲準了。

虛讀(幻讀)

虛讀(幻讀)是指在一個事務內讀取到了別的事務插入的數據,導致前後讀取不一致。
如丙存款100元未提交,這時銀行做報表統計account表中所有用戶的總額爲500元,然後丙提交了,這時銀行再統計發現帳戶爲600元了,造成虛讀同樣會使銀行不知所措,到底以哪個爲準。可將例子簡化爲:讀整個表,即表的行數,例如第一次讀某個表有3條記錄,第二次讀該表又有4條記錄。
數據庫共定義了四種隔離級別,應用《高性能mysql》一書中有說明

在這裏插入圖片描述
在這裏插入圖片描述

Serializable(串行化):可避免髒讀、不可重複讀、虛讀情況的發生。
Repeatable read(可重複讀):可避免髒讀、不可重複讀情況的發生。
Read committed(讀已提交):可避免髒讀情況發生。
Read uncommitted(讀未提交):最低級別,以上情況均無法保證。

在這裏插入圖片描述

修改隔離級別

下面說說修改事務隔離級別的方法:

全局修改

全局修改,修改my.ini(或mysql.ini)配置文件,在最後加上:

#可選參數有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.

[mysqld]
transaction-isolation = REPEATABLE-READ

MySQL默認的隔離級別爲REPEATABLE-READ,並且是嚴格遵循數據庫規範設計的,即支持4種隔離級別;
Oracle默認的隔離級別爲Read committed,並且不支持這4種隔離級別,只支持這4種隔離級別中的2種,Read committed和Serializable。

對當前session修改

對當前session修改,在登錄mysql客戶端後,執行命令:

set session transaction isolation level read uncommitted; // 設置當前事務隔離級別

注意:session是不能掉的,不然你設置不會成功,MySQL的隔離級別還是默認的隔離級別——REPEATABLE-READ,如下所示:
在這裏插入圖片描述

查詢當前事務隔離級:

select @@tx_isolation; // 查詢當前事務隔離級別

下面,將利用MySQL的客戶端程序,分別測試幾種隔離級別。測試數據庫爲day16,表爲account;表如下:
在這裏插入圖片描述
兩個命令行客戶端分別爲a(黑色背景窗口),b(藍色背景窗口);不斷改變b的隔離級別,在a端修改數據。
將b的隔離級別設置爲read uncommitted(未提交讀)
在a未更新數據之前,b客戶端

在這裏插入圖片描述
a更新數據,a向b轉帳100元
在這裏插入圖片描述
此時b查詢自己的帳戶,就會發現自己多了100元錢,出現了髒讀(這個事務讀取到了別的事務未提交的數據)
在這裏插入圖片描述
如果a等b走後再回滾 
在這裏插入圖片描述
此時b查詢自己的帳戶,發現又少掉了100元錢,兩次讀取的數據不一樣,出現不可重複讀現象
在這裏插入圖片描述
a提交完事務,再開啓一個事務,向表account中新增一條記錄
在這裏插入圖片描述
此時b再次查詢account表,發現表account中多了一條記錄,出現幻讀現象
在這裏插入圖片描述
將客戶端b的事務隔離級別設置爲read committed(已提交讀)
在a未更新數據之前,b客戶端
在這裏插入圖片描述
a更新數據,a向b轉帳100元
在這裏插入圖片描述
b查詢自己的帳戶,金額沒有發生任何變化,說明已提交讀隔離級別解決了髒讀的問題
在這裏插入圖片描述
a此刻提交事務
在這裏插入圖片描述
b再次查詢自己的帳戶,發現自己又多了100元錢,這時就發生不可重複讀(指這個事務讀取到了別的事務提交的數據)
在這裏插入圖片描述
a再開啓一個事務,向表account中新增一條記錄
在這裏插入圖片描述
然後b再次查詢account表,發現表account中多了一條記錄,出現幻讀現象
在這裏插入圖片描述
將b的隔離級別設置爲repeatable read(可重複讀)
在a未更新數據之前,b客戶端
在這裏插入圖片描述
a更新數據,a向b轉帳100元
在這裏插入圖片描述
b查詢自己的帳戶,金額沒有發生任何變化,這說明repeatable read這種級別可避免髒讀
在這裏插入圖片描述
a此刻提交事務 在這裏插入圖片描述
b再次查詢自己的帳戶,金額沒有發生任何變化,這說明repeatable read這種級別還可以避免不可重複讀
在這裏插入圖片描述
a再開啓一個事務,向表account中新增一條記錄
在這裏插入圖片描述
然後b再次查詢account表,發現表中可能會多出一條ddd的記錄(也有可能不會多出一條ddd的記錄,我測試時就是這種情況),這就發生了虛讀,也就是在這個事務內讀取了別的事務插入的數據(幻讀數據)
在這裏插入圖片描述
將b的隔離級別設置爲可串行化 (Serializable)
爲可串行化 (Serializable)均可避免髒讀、不可重複讀、幻讀。避免髒讀和不可重複讀的情況我就不測試了,測試步驟同上,下面我重點講解可串行化 (Serializable)避免幻讀的情況。
事務b端
在這裏插入圖片描述
事務a端
在這裏插入圖片描述
因爲此時事務b的隔離級別設置爲serializable,開始事務後,並沒有提交,所以事務a只能等待。
事務b提交事務,事務b端
在這裏插入圖片描述
事務a端
在這裏插入圖片描述
serializable完全鎖定字段,若一個事務來查詢同一份數據就必須等待,直到前一個事務完成並解除鎖定爲止,是完整的隔離級別,會鎖定對應的數據表格,因而會有效率的問題。
Serializable隔離級別,雖然可避免所有問題,但性能、效率是最低的,原因是它採取的是鎖表的方式,即單線程的方式,即有一個事務來操作這個表了,另外一個事務只能等在外面進不來。

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