天天看點

第19/24周 鎖更新(Lock Escalations)

大家好,歡迎回到性能調優教育訓練。上2個星期我們已經讨論了SQLServer裡的悲觀和樂觀鎖。今天我想談下SQL Server裡對于鎖的一個特殊現象:所謂的鎖更新(Lock Escalations)。在我們進入那個問題的細節前,我想先談下SQL Server内部使用的鎖層級(Lock Hierarchy)。

鎖層級(Lock Hierarchy)

2個星期前,當我們開始讨論悲觀并發模式(pessimistic concurrency)時,我告訴你SQLServer在記錄層會擷取共享鎖(Shared Locks)和排它鎖(Exclusive Locks)。遺憾的是,這不是全部事實。完整事實是SQL Server會在不同粒度(granularities)獲得鎖,例如資料庫,不同頁,最後在記錄層。SQL Server實作整個所層級(lock hierarchy),如下圖所示:

第19/24周 鎖更新(Lock Escalations)

一旦你使用一個資料庫,你的會話會在資料庫上獲得一個共享鎖(Shared Lock)。那個共享鎖是需要的,是以沒有其他人可以删除資料庫,或還原資料庫備份。這些操作會被阻塞,因為你打開的會話。SQL Server不但在行層上有共享鎖和排它鎖,SQL Server也在表和頁層使用所謂的意向鎖(Intent Locks)。

  • 在行層有共享鎖(Shared Locks),在表和頁上拿到意向共享鎖(intent shared lock(IS))。
  • 在行層有排它鎖(Exclusive Locks),在表和頁上拿到意向排它鎖(intent exclusive lock(IX))。

意向鎖(Intent Locks)用來作為1個信号,表示在鎖層級(lock hierarchy)裡(很可能)有1個不相容的鎖在低一層已獲得。意向鎖是關系資料庫主要性能調優。沒有它們的話,鎖管理器需要在低1層完全進入清單,來決定高1層的鎖是否可以擷取。如果你在表層有一個意向排它鎖(IX),你就不能在表層獲得排它鎖(X),因為有些記錄在表本身裡已經是排它鎖(X):在表層獲得排它鎖(X)會阻塞,因為在表上已經有意向排它鎖(IX)。

遺憾的是這個多粒度鎖并不是免費的:在SQL Server裡每個鎖需要96 bytes,是以會消耗一些記憶體,SQL Server必須保證沒有查詢使用太多的記憶體空間,不然的話記憶體會被耗盡。這就是為什麼會有鎖更新(lock escalations)的存在。

鎖更新(Lock Escalations)

假設下列情景:你更新散布在20萬個資料頁上的1百萬條記錄。在那個情況下,你需要在記錄本身獲得1百萬個排它鎖(X),在不同頁上獲得20萬個意向排它鎖(IX),在表本身上獲得1個意向排它鎖(IX),你的查詢合計需要獲得1200001,在鎖管理器需要近110M的鎖空間——就隻對這個簡單查詢。依據記憶體占用這個方法非常危險。是以你在一層一旦獲得超過5000個鎖,SQL Server就會觸發鎖更新(Lock Escalations)——例如在記錄層。在那個情況下,SQL Server更新你個體細密度行鎖為1個粗顆粒的表鎖:

  • 個體X鎖更新為1個表的X鎖
  • 個體S鎖更新為1個表的S鎖

下圖示範了鎖更新發生前後的鎖保持情況:

第19/24周 鎖更新(Lock Escalations)

通過鎖更新記憶體占用肯定會下降——但這也會影響你資料庫的并發!在表上的排它鎖(X)意味着沒有其他人可以從你的表讀寫,在表層上的共享鎖(S)意味着你的表是隻讀的,沒有人可以寫它!你資料庫的吞吐量隻會下降!

當你在1個層獲得超過5000個鎖,SQL Server就會觸發鎖更新。這是系統硬碼限定,不同通過任何配置選項修改。自SQL Server 2008開始,你可以通過如下代碼,控制通過ALTER TABLE DDL語句的鎖更新:

1 ALTER TABLE MyTableName
2 SET
3 (
4    LOCK_ESCALATION = TABLE -- or AUTO or DISABLE
5 )
6 GO      

預設情況,SQL Server總是更新到表級别(Table選項)。如果你設定更新選項為AUTO,當你的表是分區的話,SQL Server可以更新到分區級别。但對這個選項,你要非常仔細,因為如果你用錯誤的順序通路分區,它會導緻死鎖。使用DISABLE選項,對表你停用了鎖更新——這會帶來剛才提到的所有各個副作用(記憶體消耗)。現在的問題是,你如何高效修改或删除5000條記錄而不觸發鎖更新?

  1. 逐漸更新/删除少于5000條記錄(例如在WHILE循環裡)
  2. 如果表分區的話,使用分區交換
  3. 臨時停用鎖更新,但要注意同時的記憶體耗用

小結

鎖更新(Lock Escalations)是SQL Server提供的安全保障。它們為什麼存在有個好理由,但當更新發生時,這個會引入更少并發的副作用。是以當你在寫一次處理超過5000條記錄的代碼時要非常仔細。或許你可以逐漸處理這些記錄,而不是用1個大的UPDATE/DELETE語句。如果你想了解更多鎖更新資訊,可以看下我以前寫一篇文章《鎖更新》。

下周我們繼續SQL Server裡的鎖和阻塞,講下死鎖,還有SQL Server如何處理它們。請繼續關注!

圍觀PPT:

1007_19_鎖更新.rar

注:此文章為

WoodyTu

學習MS SQL技術,收集整理相關文檔撰寫,歡迎轉載,請在文章頁面明顯位置給出此文連結!

若您覺得這篇文章還不錯請點選下右下角的推薦,有了您的支援才能激發作者更大的寫作熱情,非常感謝!

繼續閱讀