1. 程式人生 > >sql server 阻塞與鎖

sql server 阻塞與鎖

SQL Server阻塞與鎖

在討論阻塞與加鎖之前,需要先理解一些核心概念:併發性、事務、隔離級別、阻塞鎖及死鎖。

併發性是指多個程序在相同時間訪問或者更改共享資料的能力。一般情況而言,一個系統在互不干擾的情況下可以啟用的併發使用者的程序數越多,該系統的併發性就越強。就像通常所說的系統性能表現,系統同時處理的併發使用者數越多,說明系統的效能越強。當正在更改資料的程序阻止其他程序讀取該資料時,或者當讀取資料的程序阻止其它程序更改該資料時,併發行會減弱。另外,當多個程序試圖同時更改相同資料時,且無法在不犧牲資料的一致性的前提下都能成功時,併發性也會受到影響。在SQL Server中提供了兩種方法對併發性進行有效的管理:悲觀控制法和樂觀控制法。悲觀控制法認為不同程序將會試圖同時讀寫相同的資料,並且通過鎖來阻塞另一個程序對正在使用的資料進行訪問,即當一個程序讀資料時,需要通過加鎖防止其它程序對它進行寫操作,當一個程序對寫資料時,需要通過加鎖防止其它程序對它進行讀操作,這樣,讀資料程序阻塞寫資料程序,寫資料程序阻塞讀資料程序。樂觀控制方法假設系統中很少資料在讀程序和寫程序中發生交叉的情況,因此不需要對資料進行加鎖操作,這意味著讀程序不會阻塞寫程序,寫程序不會阻塞讀程序。

SQL Server預設設定是悲觀式併發控制,因此為了提高應用程式的併發性,需要對資源加鎖和解鎖,在SQL Server 2005釋出後,可以採用樂觀式的策略,之所以可以採用這種策略,是因為SQL Server可以使用資料行版本控制,允許讀程序讀取的資料是寫程序開始進行修改之前的版本。在進行併發容易產生以下幾種情況的異常:丟失更新、髒讀、不可重複讀、幻影。

1) 丟失更新(Lost Updates)

丟失更新是指兩個程序對同一個資料進行讀取操作,並且同時試圖對該資料值進行更新時,這很容易出現更新初始值錯誤的現象。例如:一個人找兩個朋友借錢,這個人銀行卡帳中的餘額為5000塊,結果這兩個朋友同時向這張銀行卡存款,假設朋友A向這張銀行卡帳戶中存5000塊,朋友B向這張銀行卡帳戶中存6000塊,當朋友A存款完成後,銀行的出納員將銀行卡帳戶中的錢修改為10000塊(5000+5000),當朋友B存款完成後,銀行的出納員將銀行卡帳戶中的錢修改為11000塊(5000+6000),由於出現更新丟失,導致損失了5000塊,這個結果顯然無法接受。

2) 髒讀(Dirty Reads)

髒讀是指對提交的資料進行讀取操作,一個程序對資料進行了修改,但在提交修改後的資料時取消了當前的操作,即沒有更新成功,但另一個程序已經讀取了修改後的值並開始使用它。例如:你有一套房產,最近由於資金緊張,想將該處房產賣掉,這樣房產中介公司的經紀人A則將該資訊寫入到公司資源池中(這樣公司所有的房產銷售人員都可以看該資訊),但過了一些時間通過另外的方式籌到了這筆資金,你又不打算賣掉這處房產,所以通知經紀人A取消該房產資訊,但此時經紀人B已經找到了買家,此時經紀人B讀取到的就是髒資料。

3) 不可重複讀(Non-Repeatable Reads)

不可重複讀是指一個程序在同一個事務中對資料進行了兩次讀取操作,這樣第二次讀到的資料很可能與第一次讀到的資料不一致。例如:超市統計當前8臺收銀機收銀總和,將每臺收銀機的金額讀取出來並相加得到總和,之後再次逐臺收銀機相加檢查統計出來的總和,結果發現這兩次計算出來的結果並不相同,因為在第一次統計結束後,有的收銀機再次收銀了,所以第二次計算出來的結果與第一次計算出來的結果不同。

4) 幻影(Phantoms)

幻影是指一個處理插入或刪除一行資料的事務,在處理該事務時,該資料行在另一個事務可讀取的範圍內。例如:在中國人才熱線上去搜索軟體測試相關職位,假設可以搜尋到20家公司,於是說準備好簡歷,向這20家公司投簡歷,然後再按E-mail地址進行搜尋,但搜尋發現有25個E-mail地址,那麼第一次搜尋中沒有找到的職位就是幻影資料行。

事務是資料庫併發控制的基本單元,可以執行一個或多個動作。事務分為顯式事務和隱式事務,顯式事務是顯式的開始一個事務並顯式的滾回或提交事務,除了顯式的事務還有隱式的了,隱式事務是資料庫自己根據情況完成的事務處理,如單獨的select、update、delete、select語句。在關係資料管理系統(Relational Database Management System,RDBMS)中,事務必須滿足ACID屬性。

1) 原子性(Atomicity)

一個事務是一個整體所有的動作都發生並都被執行,要麼全部執行成功,如果在執行過程中有一個動作失敗,那麼事務中之前所有的動作都必須回滾,就當之前的動作沒有執行。例如:做一個入庫操作,在這個事務裡,稽核入庫單和修改庫存作為一個整體,要不單據變成稽核過同時庫存增加相應的值,要不就是單據未稽核同時庫存不變。

2) 一致性(Consistency)

一致性是指事務不能違背資料庫的完整性規則,當事務處理結束後,資料庫必須處於一個一致的狀態。例如:庫存的值不能為負數,sex的欄位必須是male或female。

3) 隔離性(Isolation)

SQL Server在設計時應該注意其必須有為很多併發使用者供服務,但在從使用者的角度來看,資料集必須看像去就像該使用者是系統中唯一的使用者,每個事務都必須是完全自包含的,並且所作的修改必須對於任何其它事務是不可讀,SQL Server中關於事務的隔離有5種不同的級別,下面的內容中會詳細介紹。

4) 永續性(Durability)

永續性是指事務提交後,必須持久的儲存,即使事務處理完成後系統發生故障,如果在事務執行的過程中發生故障那麼事務就會全部撤銷,當SQL Server提交事務成功時,在提交成功的訊息返回給使用者之前,需要將相關的資訊寫入到事務日誌中。

SQL Server中關於事務的隔離有5種不同的級別:讀取未提交、讀取提交、可重複讀、可序列化和快照;

1) 讀取未提交(Read unconnitted)

這是最低隔離,它可以允許髒讀、不可重複讀和幻影,如果一個事務已經開始寫資料,則另外一個數據不允許同時進行寫操作,只允許對該事務進行讀操作,如果不介意髒讀並且希望以可能的最輕量級接觸來讀取資料,則可以使用該隔離級別,在讀取資料時,這種方式在資料上不加任何鎖。

2) 讀取提交(Read connitted)

這種隔離方式不允許髒讀,但仍可能存在不可重複讀和幻影讀取資料的事務允許其它事務繼續訪問該行資料,但未提交的寫事務則會禁止其它事務訪問該行,這是SQL Server的預設隔離級別,通常可以在效能和業務需求之間提供最佳平衡。當讀操作的語句執行完成後,所持有的鎖都會被釋放,即使是在事務內部也是如此。

3) 可重複讀(Repeatable read)

該隔離可以防止髒讀以及不可重複讀,但是幻影仍然可能發生,讀取資料的事務將會禁止寫事務,但允許讀事務,如果是寫事務則禁止任何其它的事務,如果在事務持續期間保持讀鎖,以防止其它事務修改資料,那麼實現可重複讀也是有可能的。

4) 可序列化(Serializable)

該隔離方式要求事務序列化執行,對資料只能進行序列化訪問,並且在事務持續一直保持著鎖定狀態,這樣可以有效的鎖定那此雖然不存在但位於鍵範圍內的資料行,防止所有的副作用,該隔離程度是最高級別的,不允許高併發性執行。

5) 快照(Snapshot)

快照隔離級別是唯一可用的樂觀的隔離級別,其使用的是資料行版本控制,而不是鎖定,這意味著在一個事務中,由於讀一致性可以通過行版本控制實現,因此同樣的資料總是可以像在序列化級別上一樣被讀取而不必為防止來自其他事務的更改而被鎖定。但是仍然允許更新衝突的發生,這在事務序列化執行中是不會發生的,這種衝突發生在一個快照事務內部即將改變的資料被另外一個事務併發更改時,在執行過程中SQL Server能夠自動進行檢測,如果發生衝突,則快照事務進行回滾,以防止丟失更新。

關於隔離級別與可能的異常關係見表。

 

如果希望更改預設事務隔離級別,可以使用命令:

set transaction isolation level 隔離級別名

go

例如:如果希望將隔離級別更改為repeatable read(可重複讀),使用的命令如下:

set transaction isolation level repeatable read

go

無論那種模型,鎖在併發控制中是必需的,SQL Server會自動處理鎖,在SQL Server中有三種基本的粒度級別:行鎖、頁鎖和表鎖。

行鎖(row-level lock):是指鎖定一個數據頁或索引頁中的一行資料。行是可以鎖定的最小空間,行級鎖佔用的資料資源最少,所以在事務的處理過程中,允許其它事務繼續操縱同一個表或者同一個頁的其它資料,大大降低了其它事務等待處理的時間,提高了系統的併發性。

頁鎖(page lock):用於鎖定一個頁,一個頁由8KB的資料或索引資訊組成。在事務的操縱過程中,無論事務處理資料的多少,每一次都鎖定一頁,在這個頁上的資料不能被其它事務操縱,頁級鎖鎖定的資源比行級鎖鎖定的資料資源多,在頁級鎖中,即使是一個事務只操縱頁上的一行資料,那麼該頁上的其它資料行也不能被其它事務使用。

表鎖(table lock):用於鎖定包含資料與索引的整個表。用於表級別的鎖定。事務在操縱某一個表的資料時,鎖定了這個資料所在的整個表,其它事務不能訪問該表中的其它資料,當事務處理的資料量比較大時,一般使用表級鎖,表級鎖的特點是使用比較少的系統資源,但是卻佔用比較多的資料資源。

在執行過程中獲得每種鎖均佔用一定數量的系統資源,但並不是使用粒度越小的鎖越好,在某個臨界點上,使用一個較大粒度的鎖會更使用多個小粒度的鎖更有效率,當達到該臨界點時,SQL Server將鎖升級為表鎖,這個過程被稱為鎖升級。當事務中某個語句在一個物件上使用的鎖的數量超過5000時,SQL Server將試著升級到表鎖,SQL Server還會在出現記憶體壓力時試圖升級鎖,升級時都會升級到表鎖,一般情況下,查詢優化器會在建立執行計劃時選擇最合適的鎖粒度,因此不會經常發生鎖升級。

SQL Server支援的鎖定模式包括:共享鎖、排他鎖、更新鎖、架構鎖和意向鎖。

共享鎖(S):用於保護資源,只允許對其進行只讀資料操作,當資源上存在共享(S)鎖時,其它事務均不能修改資料。共享鎖是非獨佔的,即允許多個併發事務讀取該鎖定的資源,預設情況下,資料被讀取後,SQL Server立即釋放共享鎖。例如,執行查詢“SELECT * FROM TEST”時,首先鎖定第一頁,讀取之後,釋放對第一頁的鎖定,然後鎖定第二頁。這樣,就允許在讀操作過程中,修改未被鎖定的第一頁。但是,事務隔離級別連線選項設定和SELECT語句中的鎖定設定都可以改變SQL Server的這種預設設定。例如,“SELECT * FROM TEST HOLDLOCK”就要求在整個查詢過程中,保持對錶的鎖定,直到查詢完成才釋放鎖定。

排他鎖(X):是資料修改時需要申請的鎖,例如插入、更新或刪除,確保不能同時對同一資源進行多個更新,只能對資料加上一個排他鎖。如果事務T對資料A加上排他鎖後,則其它事務不能再對A加任何型別的鎖,獲准排他鎖的事務既能讀資料,又能修改資料。

其它的鎖模式要麼是以上兩者的混合,要麼用來對上述的兩種鎖模式提供堅持,主要包括:更新鎖、架構鎖和意向鎖。

更新鎖(U):防止常見形式的死鎖,當一個程序需要在更新之前讀取資料時需要申請更新鎖,每次只有一個事務可以獲得資源上的更新鎖,如果事務修改資源,則更新鎖將轉換為排他鎖。更新鎖不會阻塞共享鎖。

架構鎖:在執行依賴於表架構的操作時使用。架構鎖的型別是架構修改 (Sch-M) 和架構穩定性 (Sch-S)。執行表的資料定義語言操作時(如新增列或除去表)時,使用架構修改(Sch-M)鎖,當編譯查詢時,使用架構穩定性(Sch-S)鎖,架構穩定性(Sch-S)鎖不阻塞任何事務鎖,包括排它鎖。因此在編譯查詢時,其它事務(包括在表上有排它鎖的事務)都能繼續執行,但不能在表上執行資料定義語言操作。

意向鎖:建立鎖層次結構,這些鎖指示事務正在處理層次結構中較低級別的某些資源,而不是所有資源,較低級別的資源將具有共享鎖、更新鎖或排他鎖。意向鎖說明SQL Server有在資源的低層獲得共享鎖或排它鎖的意向。例如,表級的共享意向鎖說明事務意圖將排它鎖釋放到表中的頁或者行。意向鎖又可以分為共享意向鎖、獨佔意向鎖和共享式獨佔意向鎖。共享意向鎖說明事務意圖在共享意向鎖所鎖定的低層資源上放置共享鎖來讀取資料,獨佔意向鎖說明事務意圖在共享意向鎖所鎖定的低層資源上放置排它鎖來修改資料。共享式排它鎖說明事務允許其他事務使用共享鎖來讀取頂層資源,並意圖在該資源低層上放置排它鎖。

通常有兩種情況會導致加鎖問題:阻塞鎖和死鎖。阻塞鎖是當一個程序對資源進行了加鎖,但該資源已經被另一個程序鎖定,這就產生了鎖衝突,即出現了阻塞,當然SQL Server在處理併發時本身就是採用該方式對資源地行處理,但是如果第一個程序需要等待很長時間才能獲得鎖的話,那麼顯然就產生了效能問題,在工作中還可能產生一條阻塞程序鏈,當統計中發現阻塞鎖非常高的時候,那麼阻塞鎖就成了影響效能的問題了。那麼程序在等待多久獲得鎖資源才是合理的呢?SQL Server有一個鎖超時的原理,預設情況下SQL Server鎖定超時的時間為兩秒鐘,除了嘗試訪問資料並有可能發生超時以外,在對資源進行鎖定之前,沒有辦法測試該資源是否已被鎖定。LOCK_TIMEOUT設定允許應用程式設定語句等待被阻塞資源的最長等待時間,當語句等待的時間超過LOCK_TIMEOUT設定的時間時,將自動取消阻塞的語句,並將錯誤訊息 SSCE_M_LOCKTIMEOUT返回給應用程式。如果應用程式不捕獲錯誤,它可以繼續執行,但將不會知道事務中的單個語句已被取消。由於事務中後面的語句可能依賴於未執行的語句,因此可能會出現錯誤。若要設定會話的當前LOCK_TIMEOUT設定,請執行SET LOCK_TIMEOUT語法,如下列程式碼示例所示:

SET LOCK_TIMEOUT 2000

死鎖是指當兩個程序各自擁有一個鎖,而這個鎖是對方繼續執行所需要的,這樣就會出現兩個程序相互阻止對方執行的情況,這就會出現死鎖,如果對這種情況不進行處理的話,那麼就將無限期的等待下去,在SQL Server中內建了死鎖探測,每5秒鐘鎖監視器就會檢查死鎖狀態,如果發現死鎖,這個檢查就會變的很頻繁,SQL Server通常選擇終止回滾成本較低的程序來解決死鎖問題。如果是在SQL Server Mobile中則使用鎖定超時處理死鎖,因為SQL Server Mobile中沒有死鎖探測器,如果一個事務試圖鎖定某個資源但無法在鎖定超時時間內實現鎖定,就會出現錯誤。鎖定超時可確保一個事務不會無限期地等待由另一個事務控制的資源,可以使用LOCK_TIMEOUT修改鎖定超時。如果一個死鎖涉及到兩個事務,則其中一個事務會等待資源超時,同時會出現錯誤。超時的事務仍然處於活動狀態;它不會被提交或回滾。另一個事務於是獲得了所需的鎖,然後繼續執行