天天看點

事務的隔離級别通俗演義

 一.隔離級别

在操作資料時可能帶來 3 個副作用,分别是髒讀、不可重複讀、幻讀。為了避免這 3 中副作用的發生,在标準的 SQL 語句中定義了 4 種隔離級别,分别是未送出讀、已送出讀、可重複讀、可序列化。而在 spring 事務中提供了 5 種隔離級别來對應在 SQL 中定義的 4 種隔離級别,如下:

隔離級别 意義
ISOLATION_DEFAULT 使用後端資料庫預設的隔離級别
ISOLATION_READ_UNCOMMITTED 允許讀取未送出的資料(對應未送出讀),可能導緻髒讀、不可重複讀、幻讀
ISOLATION_READ_COMMITTED 允許在一個事務中讀取另一個已經送出的事務中的資料(對應已送出讀)。可以避免髒讀,但是無法避免不可重複讀和幻讀
ISOLATION_REPEATABLE_READ 一個事務不可能更新由另一個事務修改但尚未送出(復原)的資料(對應可重複讀)。可以避免髒讀和不可重複讀,但無法避免幻讀
ISOLATION_SERIALIZABLE 這種隔離級别是所有的事務都在一個執行隊列中,依次順序執行,而不是并行(對應可序列化)。可以避免髒讀、不可重複讀、幻讀。但是這種隔離級别效率很低,是以,除非必須,否則不建議使用。
Read uncommitted 讀未送出

     今天隔壁老王格外開心,因為今天是老婆給他發這個月的零花錢的日子,老婆告訴老王現在打錢,老王看到打到銀行卡多了500元,本來每月隻有是300,居然這個月發了500,正在竊喜之際,老婆臨時想到購物車還沒清空,于是復原事務,改成了300,并送出了。(對應我們所說的髒讀 ,兩個并發的事務,“事務A:老婆給老王發零花錢”、“事務B:老王查詢銀行賬戶”,事務B讀取了事務A尚未送出的資料(500元)。當隔離級别設定為Read uncommitted 時,就可能出現髒讀,如何避免髒讀,請看下一個隔離級别)。

Read committed 讀已送出

     情人節快到了,老王帶着小三來到一家酒店,前台小姐姐檢視系統,顯示行卡餘額300,老王正在竊喜之際,就在此時,老婆在清空購物車時發現錢不夠了,于是把老王卡裡的200元通過網上轉賬劃到自己的卡上并送出了,老王送出訂單發現系統顯示自己餘額為100元,再看看銀行卡餘額不是剛才查詢的300而是100元,小三當機提出分手,一氣之下摔門而去。(這裡對應我們所說的不可重複讀,兩個并發的事務,“事務A:老王消費”、“事務B:老王的老婆網上轉賬”,事務A事先讀取了資料,事務B緊接了更新了資料,并送出了事務,而事務A再次讀取該資料時,資料已經發生了改變。即一個事務A讀到了另一個事務B已經送出的資料(這裡是update),導緻事務A,在多次查詢的結果不一緻)。

Sql Server , Oracle資料庫的預設級别就是Read committed。

Repeatable read 重複讀

      氣急敗壞的老王找到銀行要讨個說法,櫃台的小姐姐将老王的銀行卡設定改了下,給老王說:”這下好了,一旦銀行系統開始讀取你卡上的資訊,其他人就不能操作修改卡上的餘額了(解決了不可重複讀的問題)”,老王半信半疑的回了家。路上,老王一個勁的給小三道歉,并答應現在給小三轉100元過去當做賠禮,然而,精明的老婆此時此刻正在另一家銀行檢視老王銀行卡上的消費,發現老王最近沒在外面用銀行卡花錢,心裡想着待會買點菜回去做飯獎勵一下老王。另一邊邊老王的100元已經打給了小三,老婆并告訴櫃台将老王的流水列印一下,但是此時流水中居然出現給陌生人轉錢100元的字段,老婆有點不解,以為出現了幻覺。(這裡對應我們所說的可重複讀,還是上集故事兩個并發的事務,“事務A:老王消費”、“事務B:老王的老婆網上轉賬”,事務A事先讀取了資料,事務B此時不可以更新資料,而事務A再次讀取該資料時,資料沒有改變。重複讀雖然解決了不可重複讀的問題,但是又出現了幻讀的問題,即一個事務(老婆查流水)讀到了另一個事務(老王給小三轉賬)已經送出的資料(這裡是insert,給表裡新添加了一條資料),導緻另一個事務(老婆查流水),在事務中多次查詢的結果不一緻)。

Mysql的預設隔離級别就是Repeatable read。

Serializable 序列化

老王回家後,老婆問出了老王今天下午幾點幾分到底給誰轉賬,老王打馬虎眼,心裡嘀咕,這都知道我幾點幾分了轉賬了,莫非是對我起了疑心,應該不會,為了防範萬一,于是,第二天去銀行又更新銀行卡了,櫃台告訴他:“更新好了,你的卡已經是最高安全級别了,你在轉賬時,沒轉完前,别人不可以檢視你的流水”。這下老王放心了,但是誰知小三早已拉黑了老王,并換了手機号。(Serializable 是最高的事務隔離級别,同時代價也花費最高,性能很低,一般很少使用,在該級别下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀)。

事務的隔離級别通俗演義

二. 隻讀

如果在一個事務中所有關于資料庫的操作都是隻讀的,也就是說,這些操作隻讀取資料庫中的資料,而并不更新資料,那麼應将事務設為隻讀模式( READ_ONLY_MARKER ) , 這樣更有利于資料庫進行優化 。

因為隻讀的優化措施是事務啟動後由資料庫實施的,是以,隻有将那些具有可能啟動新事務的傳播行為(PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事務标記成隻讀才有意義。

如果使用 Hibernate 作為持久化機制,那麼将事務标記為隻讀後,會将 Hibernate 的 flush 模式設定為 FULSH_NEVER, 以告訴Hibernate 避免和資料庫之間進行不必要的同步,并将所有更新延遲到事務結束。

三.事務逾時

如果一個事務長時間運作,這時為了盡量避免浪費系統資源,應為這個事務設定一個有效時間,使其等待數秒後自動復原。與設

置“隻讀”屬性一樣,事務有效屬性也需要給那些具有可能啟動新事物的傳播行為的方法的事務标記成隻讀才有意義。

四.傳播行為定義了被調用方法的事務邊界。

傳播行為 意義
PROPERGATION_MANDATORY 表示方法必須運作在一個事務中,如果目前事務不存在,就抛出異常
PROPAGATION_NESTED 表示如果目前事務存在,則方法應該運作在一個嵌套事務中。否則,它看起來和PROPAGATION_REQUIRED 看起來沒什麼倆樣
PROPAGATION_NEVER 表示方法不能運作在一個事務中,否則抛出異常
PROPAGATION_NOT_SUPPORTED 表示方法不能運作在一個事務中,如果目前存在一個事務,則該方法将被挂起
PROPAGATION_REQUIRED 表示目前方法必須運作在一個事務中,如果目前存在一個事務,那麼該方法運作在這個事務中,否則,将建立一個新的事務
PROPAGATION_REQUIRES_NEW 表示目前方法必須運作在自己的事務中,如果目前存在一個事務,那麼這個事務将在該方法運作期間被挂起
PROPAGATION_SUPPORTS 表示目前方法不需要運作在一個是事務中,但如果有一個事務已經存在,該方法也可以運作在這個事務中

轉載于:https://www.cnblogs.com/fengtangjiang/p/11099086.html