數據庫是要被廣大客戶所共享訪問的,那麼在數據庫操做過程當中很可能出現如下幾種不肯定狀況。數據庫
更新丟失(Lost update)
兩個事務都同時更新一行數據,可是第二個事務卻中途失敗退出,致使對數據的兩個修改都失效了。這是由於系統沒有執行任何的鎖操做,所以併發事務並無被隔離開來。
髒讀(Dirty Reads)
一個事務開始讀取了某行數據,可是另一個事務已經更新了此數據但沒有可以及時提交。這是至關危險的,由於極可能全部的操做都被回滾。
不可重複讀(Non-repeatable Reads)
一個事務對同一行數據重複讀取兩次,可是卻獲得了不一樣的結果。它包括如下狀況:
(1) 事務T1讀取某一數據後,事務T2對其作了修改,當事務T1再次讀該數據時獲得與前一次不一樣的值。
(2) 幻讀(Phantom Reads):事務在操做過程當中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺乏了第一次查詢中出現的數據(這裏並不要求兩次查詢的SQL語句相同)。這是由於在兩次查詢過程當中有另一個事務插入數據形成的。session
解決方案併發
爲了不上面出現的幾種狀況,在標準SQL規範中,定義了4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣。
未受權讀取
也稱爲讀未提交(Read Uncommitted):容許髒讀取,但不容許更新丟失。若是一個事務已經開始寫數據,則另一個事務則不容許同時進行寫操做,但容許其餘事務讀此行數據。該隔離級別能夠經過「排他寫鎖」實現。
受權讀取
也稱爲讀提交(Read Committed):容許不可重複讀取,但不容許髒讀取。這能夠經過「瞬間共享讀鎖」和「排他寫鎖」實現。讀取數據的事務容許其餘事務繼續訪問該行數據,可是未提交的寫事務將會禁止其餘事務訪問該行。
可重複讀取
可重複讀取(Repeatable Read):禁止不可重複讀取和髒讀取,可是有時可能出現幻影數據。這能夠經過「共享讀鎖」和「排他寫鎖」實現。讀取數據的事務將會禁止寫事務(但容許讀事務),寫事務則禁止任何其餘事務。
序列化
序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,但不能併發執行。若是僅僅經過「行級鎖」是沒法實現事務序列化的,必須經過其餘機制保證新插入的數據不會被剛執行查詢操做的事務訪問到。
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed。它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使不可重複讀、虛讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。函數
語法性能
SET TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE [..] } |
一次只能設置一個隔離級別選項,並且設置的選項將一直對那個鏈接始終有效,直到顯式更改該選項爲止。事務中執行的全部讀取操做都會在指定的隔離級別的規則下運行,除非語句的 FROM 子句中的表提示爲表指定了其餘鎖定行爲或版本控制行爲。優化
事務隔離級別定義了可爲讀取操做獲取的鎖類型。針對 READ COMMITTED 或 REPEATABLE READ 獲取的共享鎖一般爲行鎖,儘管當讀取引用了頁或表中大量的行時,行鎖能夠升級爲頁鎖或表鎖。若是某行在被讀取以後由事務進行了修改,則該事務會獲取一個用於保護該行的排他鎖,而且該排他鎖在事務完成以前將一直保持。例如,若是 REPEATABLE READ 事務具備用於某行的共享鎖,而且該事務隨後修改了該行,則共享行鎖便會轉換爲排他行鎖。spa
當事務進行時,您能夠隨時將事務從一個隔離級別更改成另外一個隔離級別。將事務從一個隔離級別更改成另外一個隔離級別以後,便會根據新級別的規則對更改後讀取的資源執行保護。更改前讀取的資源將繼續根據先前級別的規則進行保護,例如,一個事務由 REPEATABLE READ 更改成 SERIALIZABLE。由更改前發出的 SELECT 語句讀取的行將繼續受到行級、頁級或表級共享鎖的保護。這些鎖會繼續保持,直至事務結束。由 SELECT 語句在更改後讀取的行將受到範圍鎖的保護。版本控制
該表顯示事務從一個隔離級別更改成另外一個隔離級別時的鎖定行爲。code
更改前的隔離級別 | 更改後的隔離級別 |
---|---|
READ UNCOMMITTED對象 |
READ UNCOMITTED: 未更改。 READ COMMITTED: 該行爲取決於 READ_COMMITTED_SNAPSHOT 數據庫選項的設置: 若是爲 OFF,事務將獲取共享鎖,並在讀取期間保留鎖。 若是爲 ON,事務會使用行版本控制。 SNAPSHOT: 事務必須已做爲 SNAPSHOT 啓動。事務將會失敗,並將回滾全部更改。 REPEATABLE READ: 如今該事務將獲取共享鎖,並在事務期間保留鎖。 SERIALIZABLE: 如今該事務將獲取範圍鎖,並在事務期間保留鎖。 |
READ COMMITTED |
READ UNCOMITTED: 事務再也不獲取用於讀取操做的鎖。 READ COMMITTED: 未更改。 SNAPSHOT: 事務必須已做爲 SNAPSHOT 啓動。事務將會失敗,並將回滾全部更改。 REPEATABLE READ: 如今該事務將獲取共享鎖,並在事務期間保留鎖。 SERIALIZABLE: 如今該事務將獲取範圍鎖,並在事務期間保留鎖。 |
SNAPSHOT |
READ UNCOMITTED: 事務再也不使用行版本控制,而且再也不獲取用於讀取操做的鎖。 READ COMMITTED: 該行爲取決於 READ_COMMITTED_SNAPSHOT 數據庫選項的設置: 若是爲 OFF,事務將獲取共享鎖,並在讀取期間保留鎖。 若是爲 ON,事務會使用行版本控制。 SNAPSHOT: 未更改。 REPEATABLE READ: 該事務再也不使用行版本控制。如今它獲取了共享鎖,並在事務執行期間一直保持該鎖。 SERIALIZABLE: 該事務再也不使用行版本控制。如今它獲取了範圍鎖,並在事務執行期間一直保持該鎖。 |
REPEATABLE READ |
READ UNCOMITTED: 該事務在讀取操做時再也不獲取鎖。在 REPEATABLE READ 下獲取的共享鎖保留到事務結束。 READ COMMITTED: 該行爲取決於 READ_COMMITTED_SNAPSHOT 數據庫選項的設置: 若是爲 OFF,事務將獲取共享鎖,並在讀取期間保留這些新鎖。 若是爲 ON,事務會使用行版本控制。 在 REPEATABLE READ 下獲取的共享鎖保留到事務結束。 SNAPSHOT: 事務必須已做爲 SNAPSHOT 啓動。事務將會失敗,並將回滾全部更改。 REPEATABLE READ: 未更改。 SERIALIZABLE: 如今該事務將獲取範圍鎖,並在事務期間保留鎖。在 REPEATABLE READ 下獲取的共享鎖保留到事務結束。 |
SERIALIZABLE |
READ UNCOMITTED: 該事務在讀取操做時再也不獲取鎖。在 SERIALIZABLE 下獲取的範圍鎖保留到事務結束。 READ COMMITTED: 該行爲取決於 READ_COMMITTED_SNAPSHOT 數據庫選項的設置: 若是爲 OFF,事務將獲取共享鎖,並在讀取期間保留這些新鎖。 若是爲 ON,事務會使用行版本控制。 在 SERIALIZABLE 級別下獲取了範圍鎖,而且該鎖一直保持到事務結束。 SNAPSHOT: 事務必須已做爲 SNAPSHOT 啓動。事務將會失敗,並將回滾全部更改。 REPEATABLE READ: 如今,事務獲取了共享鎖,並在事務執行期間一直保持該鎖。在 SERIALIZABLE 下獲取的範圍鎖保留到事務結束。 SERIALIZABLE: 未更改。 |
若是在存儲過程、觸發器、用戶定義函數或用戶定義類型中發出 SET TRANSACTION ISOLATION LEVEL,則當對象返回控制時,隔離級別會重設爲在調用對象時有效的級別。例如,若是在批處理中設置 REPEATABLE READ,而且該批處理調用一個將隔離級別設置爲 SERIALIZABLE 的存儲過程,則當該存儲過程將控制返回給該批處理時,隔離級別就會恢復爲 REPEATABLE READ。
當您使用 sp_bindsession 綁定兩個會話時,每一個會話都會保留它自身的隔離級別設置。使用 SET TRANSACTION ISOLATION LEVEL 更改某個會話的隔離級別設置時,不會影響與該會話綁定的其餘任何會話的設置。
SET TRANSACTION ISOLATION LEVEL 會在執行或運行時生效,而不是在分析時生效。
對錶執行的優化大容量導入操做會阻塞在下列隔離級別下運行的查詢:
參數
指定語句能夠讀取已由其餘事務修改但還沒有提交的行。
在 READ UNCOMMITTED 級別運行的事務,不會發出共享鎖來防止其餘事務修改當前事務讀取的數據。READ UNCOMMITTED 事務也不會被排他鎖阻塞,排他鎖會禁止當前事務讀取其餘事務已修改但還沒有提交的行。設置此選項以後,能夠讀取未提交的修改,這種讀取稱爲髒讀。在事務結束以前,能夠更改數據中的值,行也能夠出如今數據集中或從數據集中消失。該選項的做用與在事務內全部 SELECT 語句中的全部表上設置 NOLOCK 相同。這是隔離級別中限制最少的級別。
在 SQL Server 2005 中,您還可使用下列任意一種方法,在保護事務不髒讀未提交的數據修改的同時儘可能減小鎖定爭用:
指定語句不能讀取已由其餘事務修改但還沒有提交的數據。這樣能夠避免髒讀。其餘事務能夠在當前事務的各個語句之間更改數據,從而產生不可重複讀取和幻像數據。該選項是 SQL Server 的默認設置。
READ COMMITTED 的行爲取決於 READ_COMMITTED_SNAPSHOT 數據庫選項的設置:
當 READ_COMMITTED_SNAPSHOT 數據庫選項設置爲 ON 時,您可使用 READCOMMITTEDLOCK 表提示爲 READ_COMMITTED 隔離級別上運行的事務中的各語句請求共享鎖,而不是行版本控制。
指定語句不能讀取已由其餘事務修改但還沒有提交的行,而且指定,其餘任何事務都不能在當前事務完成以前修改由當前事務讀取的數據。
對事務中的每一個語句所讀取的所有數據都設置了共享鎖,而且該共享鎖一直保持到事務完成爲止。這樣能夠防止其餘事務修改當前事務讀取的任何行。其餘事務能夠插入與當前事務所發出語句的搜索條件相匹配的新行。若是當前事務隨後重試執行該語句,它會檢索新行,從而產生幻讀。因爲共享鎖一直保持到事務結束,而不是在每一個語句結束時釋放,因此併發級別低於默認的 READ COMMITTED 隔離級別。此選項只在必要時使用。
指定事務中任何語句讀取的數據都將是在事務開始時便存在的數據的事務上一致的版本。事務只能識別在其開始以前提交的數據修改。在當前事務中執行的語句將看不到在當前事務開始之後由其餘事務所作的數據修改。其效果就好像事務中的語句得到了已提交數據的快照,由於該數據在事務開始時就存在。
除非正在恢復數據庫,不然 SNAPSHOT 事務不會在讀取數據時請求鎖。讀取數據的 SNAPSHOT 事務不會阻止其餘事務寫入數據。寫入數據的事務也不會阻止 SNAPSHOT 事務讀取數據。
在數據庫恢復的回滾階段,若是嘗試讀取由其餘正在回滾的事務鎖定的數據,則 SNAPSHOT 事務將請求一個鎖。在事務完成回滾以前,SNAPSHOT 事務會一直被阻塞。當事務取得受權以後,便會當即釋放鎖。
必須將 ALLOW_SNAPSHOT_ISOLATION 數據庫選項設置爲 ON,才能開始一個使用 SNAPSHOT 隔離級別的事務。若是使用 SNAPSHOT 隔離級別的事務訪問多個數據庫中的數據,則必須在每一個數據庫中將 ALLOW_SNAPSHOT_ISOLATION 都設置爲 ON。
不能將經過其餘隔離級別開始的事務設置爲 SNAPSHOT 隔離級別,不然將致使事務停止。若是一個事務在 SNAPSHOT 隔離級別開始,則能夠將它更改成另外一個隔離級別,而後再返回 SNAPSHOT。一個事務從執行 BEGIN TRANSACTION 語句開始。
在 SNAPSHOT 隔離級別下運行的事務能夠查看由該事務所作的更改。例如,若是事務對錶執行 UPDATE,而後對同一個表發出 SELECT 語句,則修改後的數據將包含在結果集中。
指定:
範圍鎖處於與事務中執行的每一個語句的搜索條件相匹配的鍵值範圍以內。這樣能夠阻止其餘事務更新或插入任何行,從而限定當前事務所執行的任何語句。這意味着若是再次執行事務中的任何語句,則這些語句便會讀取同一組行。在事務完成以前將一直保持範圍鎖。這是限制最多的隔離級別,由於它鎖定了鍵的整個範圍,並在事務完成以前一直保持範圍鎖。由於併發級別較低,因此應只在必要時才使用該選項。該選項的做用與在事務內全部 SELECT 語句中的全部表上設置 HOLDLOCK 相同。