大家好,歡迎回到性能調優培訓。上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),如下圖所示:
一旦你使用一個數據庫,你的會話會在數據庫上獲得一個共享鎖(Shared Lock)。那個共享鎖是需要的,因此沒有其他人可以刪除數據庫,或還原數據庫備份。這些操作會被阻塞,因為你打開的會話。SQL Server不但在行層上有共享鎖和排它鎖,SQL Server也在表和頁層使用所謂的意向鎖(Intent Locks)。
意向鎖(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),你的查詢合計需要獲得120001,在鎖管理器需要近110M的鎖空間——就只對這個簡單查詢。依據內存占用這個方法非常危險。因此你在一層一旦獲得超過5000個鎖,SQL Server就會觸發鎖升級(Lock Escalations)——例如在記錄層。在那個情況下,SQL Server升級你個體細密度行鎖為1個粗顆粒的表鎖:
下圖演示了鎖升級發生前后的鎖保持情況:
通過鎖升級內存占用肯定會下降——但這也會影響你數據庫的并發!在表上的排它鎖(X)意味著沒有其他人可以從你的表讀寫,在表層上的共享鎖(S)意味著你的表是只讀的,沒有人可以寫它!你數據庫的吞吐量只會下降!
當你在1個層獲得超過5000個鎖,SQL Server就會觸發鎖升級。這是系統硬碼限定,不同通過任何配置選項修改。自SQL Server 2008開始,你可以通過如下代碼,控制通過ALTER TABLE DDL語句的鎖升級:
1 ALTER TABLE MyTableName2 SET3 (4 LOCK_ESCALATION = TABLE -- or AUTO or DISABLE5 )6 GO
默認情況,SQL Server總是升級到表級別(Table選項)。如果你設置升級選項為AUTO,當你的表是分區的話,SQL Server可以升級到分區級別。但對這個選項,你要非常仔細,因為如果你用錯誤的順序訪問分區,它會導致死鎖。使用DISABLE選項,對表你停用了鎖升級——這會帶來剛才提到的所有各個副作用(內存消耗)。現在的問題是,你如何高效修改或刪除5000條記錄而不觸發鎖升級?
鎖升級(Lock Escalations)是SQL Server提供的安全保障。它們為什么存在有個好理由,但當升級發生時,這個會引入更少并發的副作用。因此當你在寫一次處理超過5000條記錄的代碼時要非常仔細。或許你可以逐步處理這些記錄,而不是用1個大的UPDATE/DELETE語句。如果你想了解更多鎖升級信息,可以看下我以前寫一篇文章《鎖升級》。
下周我們繼續SQL Server里的鎖和阻塞,講下死鎖,還有SQL Server如何處理它們。請繼續關注!
新聞熱點
疑難解答