事務的特性,事務的隔離級別和傳播行為,為什麼要有隔離級別
事務(Transaction)概念:一個sql語句就是一個事務,事務可以保證一組sql語句要麼都成功,要麼都失敗。
事務,就是一組操作資料庫的動作集合。事務是現代資料庫理論中的核心概念之一。
如果一組處理步驟或者全部發生或者一步也不執行,我們稱該組處理步驟為一個事務。
當所有的步驟像一個操作一樣被完整地執行,我們稱該事務被提交。
由於其中的一部分或多步執行失敗,導致沒有步驟被提交,則事務必須回滾到最初的系統狀態。
特性:
1. 原子性:一個事務中的所有語句,應該做到:要麼全執行要麼一個都不執行;
2. 一致性:讓資料保持邏輯上的合理性。比如一個商品出庫時,既要讓商品庫中的該商品數量減一,又要讓對應使用者的購物車中的該商品加一;
事物的執行使得資料庫從一種正確狀態轉換成另外一種正確狀態;
3. 隔離性:如果多個事務同時併發執行,但每個事務就像各自獨立執行一樣。在事物正確提交之前,不允許把事物對該資料的改變提供給任何其他事物,即在事物正確提交之前,它可能的結果不應該顯示給其他事物。
4. 永續性:一個事務執行成功,則對資料來說,應該是一個明確的硬碟資料更改(而不僅僅是記憶體中的變化)
事物正確提交之後,其結果將永遠儲存在資料庫之中,即使在事物提交之後有了其他故障,事物的處理結果也會得到儲存。
作用:
事物管理對於企業級應用而言至關重要,它保證了使用者的每一次操作都是可靠的,即便出現了異常的訪問情況,也不至於破壞後臺資料的完整性。就像銀行的自動提款機
併發下事物會產生的問題
舉個例子,事物A和事物B操縱的是同一個資源,事物A有若干個子事物,事物B也有若干個子事物,事物A和事物B在高併發的情況下,會出現各種各樣的問題。"各種各樣的問題",總結一下主要就是五種:第一類丟失更新、第二類丟失更新、髒讀、不可重複讀、幻讀。五種之中,第一類丟失更新、第二類丟失更新不重要,不講了,講一下髒讀、不可重複讀和幻讀。
1、髒讀
所謂髒讀,就是指事物A讀到了事物B還沒有提交的資料,比如銀行取錢,事物A開啟事物,此時切換到事物B,事物B開啟事物-->取走100元,此時切換回事物A,事物A讀取的肯定是資料庫裡面的原始資料,因為事物B取走了100塊錢,並沒有提交,資料庫裡面的賬務餘額肯定還是原始餘額,這就是髒讀。
2、不可重複讀
所謂不可重複讀,就是指在一個事物裡面讀取了兩次某個資料,讀出來的資料不一致。還是以銀行取錢為例,事物A開啟事物-->查出銀行卡餘額為1000元,此時切換到事物B事物B開啟事物-->事物B取走100元-->提交,資料庫裡面餘額變為900元,此時切換回事物A,事物A再查一次查出賬戶餘額為900元,這樣對事物A而言,在同一個事物內兩次讀取賬戶餘額資料不一致,這就是不可重複讀。
3、幻讀
所謂幻讀,就是指在一個事物裡面的操作中發現了未被操作的資料。比如學生資訊,事物A開啟事物-->修改所有學生當天簽到狀況為false,此時切換到事物B,事物B開啟事物-->事物B插入了一條學生資料,此時切換回事物A,事物A提交的時候發現了一條自己沒有修改過的資料,這就是幻讀,就好像發生了幻覺一樣。幻讀出現的前提是併發的書屋中有失誤發生了插入、刪除操作。
為什麼要有隔離級別:
事物隔離級別,就是為了解決上面幾種問題而誕生的。為什麼要有事物隔離級別,因為事物隔離級別越高,在併發下會產生的問題就越少,但同時付出的效能消耗也將越大,因此很多時候必須在併發性和效能之間做一個權衡。所以設立了幾種事物隔離級別,以便讓不同的專案可以根據自己專案的併發情況選擇合適的事物隔離級別,對於在事物隔離級別之外會產生的併發問題,在程式碼中做補償。
事物隔離級別有4種,但是像Spring會提供給使用者5種,來看一下:
1、DEFAULT
預設隔離級別,每種資料庫支援的事物隔離級別不一樣,如果Spring配置事物時將isolation設定為這個值的話,那麼將使用底層資料庫的預設事物隔離級別。順便說一句,如果使用的MySQL,可以使用"select @@tx_isolation"來檢視預設的事物隔離級別
2、READ_UNCOMMITTED
讀未提交,即能夠讀取到沒有被提交的資料,所以很明顯這個級別的隔離機制無法解決髒讀、不可重複讀、幻讀中的任何一種,因此很少使用
3、READ_COMMITED
讀已提交,即能夠讀到那些已經提交的資料,自然能夠防止髒讀,但是無法限制不可重複讀和幻讀
4、REPEATABLE_READ
重複讀取,即在資料讀出來之後加鎖,類似"select * from XXX for update",明確資料讀取出來就是為了更新用的,所以要加一把鎖,防止別人修改它。REPEATABLE_READ的意思也類似,讀取了一條資料,這個事物不結束,別的事物就不可以改這條記錄,這樣就解決了髒讀、不可重複讀的問題,但是幻讀的問題還是無法解決
5、SERLALIZABLE
序列化,最高的事物隔離級別,不管多少事物,挨個執行完一個事物的所有子事物之後才可以執行另外一個事物裡面的所有子事物,這樣就解決了髒讀、不可重複讀和幻讀的問題了
網上專門有圖用表格的形式列出了事物隔離級別解決的併發問題:
再必須強調一遍,不是事物隔離級別設定得越高越好,事物隔離級別設定得越高,意味著勢必要花手段去加鎖用以保證事物的正確性,那麼效率就要降低,因此實際開發中往往要在效率和併發正確性之間做一個取捨,一般情況下會設定為READ_COMMITED,此時避免了髒讀,併發性也還不錯,之後再通過一些別的手段去解決不可重複讀和幻讀的問題就好了。
spring七個事務傳播屬性:
1.PROPAGATION_REQUIRED – 支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
2.PROPAGATION_SUPPORTS – 支援當前事務,如果當前沒有事務,就以非事務方式執行。
3.PROPAGATION_MANDATORY – 支援當前事務,如果當前沒有事務,就丟擲異常。
4.PROPAGATION_REQUIRES_NEW – 新建事務,如果當前存在事務,把當前事務掛起。
5.PROPAGATION_NOT_SUPPORTED – 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
6.PROPAGATION_NEVER – 以非事務方式執行,如果當前存在事務,則丟擲異常。
7.PROPAGATION_NESTED – 如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。
備註:常用的兩個事務傳播屬性是1和4,即PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW
關鍵詞:
1)虛讀(幻讀):在一個事務內讀取了別的事務插入的資料,導致前後讀取不一致(insert),事務1讀取記錄時事務2增加了記錄並提交,事務1再次讀取時可以看到事務2新增的記錄;
2)不可重複讀取:在一個事務內讀取表中的某一行資料,多次讀取結果不同.一個事務讀取到了另一個事務提交後的資料.事務1讀取記錄時,事務2更新了記錄並提交,事務1再次讀取時可以看到事務2修改後的記錄;
3)髒讀:指一個事務讀取了一個未提交事務的資料,事務1更新了記錄,但沒有提交,事務2讀取了更新後的行,然後事務T1回滾,現在T2讀取無效。