DB之所以設定事務隔離級別,其重要原因就是要避免一些常見的髒數據的讀寫等問題,根據不同的事務隔離級別,我們能夠在數據讀寫並發效率和數據一致性方面取得不一樣的效果;
在具體進入事務隔離級別之前,我們首先來看一下到底哪些情況是我們在DB操作中不希望看到的: Dirty Read 髒數據讀取 比如事務A的未提交(還依然緩存)的數據被事務B讀走,如果事務A失敗回滾,會導致事務B所讀取的的數據是錯誤的。
Non-Repeatable Read 數據不可重復讀取 比如事務A中兩處讀取數據-total-的值。在第一讀的時候,total是100,然後事務B就把total的數據改成200,事務A再讀一次,結果就發現,total竟然就變成200了,造成事務A數據混亂。
Phantom Read 數據幻想讀取 和Non-Repeatable Read相似,也是同一個事務中多次讀不一致的問題。但是Non-Repeatable Read的不一致是因為他所要取的數據集被改變了(比如total的數據),但是Phantom Read所要讀的數據的不一致卻不是他所要讀的數據集改變,而是他的條件數據集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候,由於事務b把一個帳號的名字由"dd"改成"ppgogo1",結果取出來了7個數據。
接下來,我們來看看標准SQL中到底規范了哪幾種事務隔離級別: Serializable 全序列化 提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能並發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
Repeatable Read 可重復讀取 禁止不可重復讀取和髒讀取,但是有時可能出現幻影數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
Read Committed 授權讀取 允許不可重復讀取,但不允許髒讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。
Read Uncommitted 未授權讀取 允許髒讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個數據則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。
從上表中,我們再總結下,各種事務隔離級別都能避免哪些我們不想看到的事情:
Dirty Read Non-Repeatable Read Phantom Read
Serializable N N N
Repeatable Read N N Y
Read Committed N Y Y
Read Uncommitted Y Y Y
隔離級別越高,越能保證數據的完整性和一致性,但是對並發性能的影響也越大。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed,它能夠避免髒讀取,而且具有較好的並發性能;這也是目前大多數DB的默認事務級別。盡管它會導致不可重復讀、虛讀和第二類丟失更新這些並發問題,在可能出現這類問題的個別場合,可以由應用程序采用悲觀鎖或樂觀鎖來控制。