鎖升級

在今天的文章裏,我想談下SQL Server裏鎖升級(Lock Escalations)。鎖升級是SQL Server使用的優化技術,用來控制在SQL Server鎖管理裏把持鎖的數量。咱們首先用SQL Server裏所謂的鎖層級(Lock Hierarchy )開始,由於那是在像SQL Server的關係數據庫裏,爲何有鎖升級概念存在的緣由。sql

鎖層級(Lock Hierarchy )

 下圖展現了SQL Server使用的鎖層級:數據庫

 

從圖裏能夠看到,鎖層級開始於數據庫層級,向下至行層級。在數據庫自己層級,你一直有一個共享鎖(Shared Lock (S) )。當你的查詢鏈接到一個數據庫(例如USE MyDatabase),共享鎖會阻止數據庫刪除,或者在那個數據庫上還原備份。當你進行一個操做時,在數據庫層級下,在表上,在頁上,在記錄上都會有鎖。併發

當你執行一個SELECT語句,在表和頁上會有一個意向共享鎖(Intent Shared Lock (IS) ),在記錄自己上有共享鎖(Shared Lock (S) )。當你進行數據修改語句(INSERT,UPDATE,DELETE),在表和頁上會有一個意向排它或更新鎖( Intent Exclusive or Update Lock (IX or IU) ),在改變的記錄上有排它或更新鎖(Exclusive or Update Lock (X or U) )。當多個線程嘗試在鎖層級裏併發獲取鎖時,SQL Server會一直獲取從頭到腳的鎖來阻止所謂的競爭危害。當你對錶進行20000條記錄的刪除操做時,如今想象下這個鎖層級會是什麼樣的?咱們來假定記錄是400 bytes長,這就意味這在8kb的頁裏恰好有20條記錄:測試

 

在數據庫上你有1個共享鎖(S),在表上有1個意向排它鎖(IX),在頁上有1000個意向排它鎖(IX)(20000條記錄散佈在1000個頁上),最後在記錄自己你有20000個排它鎖(X)。對於DELETE操做總計你獲取了21002個鎖。在SQL Server裏每一個鎖須要96 bytes的內存,所以對這個簡單的查詢須要1.9MB的鎖。當你並行執行多個查詢時,這個不會無限擴展。所以,SQL Server如今用所謂的鎖升級(Lock Escalation)實現。優化

鎖升級(Lock Escalations)

在你的鎖層級裏一旦有超過5000個鎖,SQL Server會升級這麼多的精細粒度(fine-granularity)的鎖爲簡單的粗粒度(coarse-granularity)的鎖。默認狀況下,SQL Server「老是」升級到表層級。這意味着你的鎖層級從剛纔例子的樣子,在鎖升級成功執行後,變成以下的樣子:spa

 

 

如你所見,在表自己上你只有一個大鎖。在DELETE操做的狀況下,在表層級你有一個排它鎖(X)。這會以負面傷及你數據庫的併發。在表層級把持一個排它鎖(X)意味者沒有其餘回話能夠訪問那個表——每一個其它查詢會阻塞。當你在可重讀隔離級別(Repeatable Read Isolation Level)運行你的SELECT語句,你也在把持你的共享鎖(S)直到事務結束,這也就是說只要你讀超過5000條記錄就會發生鎖升級。這裏的結果是一個共享鎖(S)在表自己!你的表只是暫時只讀,由於在表上每一個其它數據修改都會阻塞!線程

這裏還有個誤解——SQL Server會鎖升級從行層級到頁層級,最後到表層級。錯了!在SQL Server裏沒有這樣的代碼路徑存在!默認狀況下,SQL Server老是會直接升級到表層級。到頁層級的升級策略不存在。若是你的表被分區了(只針對企業版本的SQL Server),那樣的話,你能夠配置升級到分區層級。但這裏你必須很是仔細測試你的數據訪問模式,由於鎖升級到分區層級會引發死鎖。所以這個選項默認是沒啓用的。scala

自SQL Server 2008開始,你能夠控制SQL Server如何進行鎖升級——經過ALTER TABLE語句和LOCK_ESCALTATION屬性。有3個可用選項:設計

  • TABLE
  • AUTO
  • DISABLE
1 -- Controllling Lock Escalation
2 ALTER TABLE Person.Person
3 SET
4 (
5     LOCK_ESCALATION = AUTO -- or TABLE or DISABLE
6 )
7 GO

默認選項是TABLE,意味着SQL Server老是執行鎖升級到表層級——即便這個表已被分區。若是你的表已被分區,你想設置分區層級的鎖升級(由於你已經測試了你的數據訪問模式,用它你不會引發死鎖),那麼你能夠修改選項爲AUTOAUTO意味着你的鎖升級會執行到分區層級,若是表被分區的話,不然就到表層級。使用DISABLE選項你能夠徹底禁用那個表的鎖升級。可是禁用鎖升級並非最好的選項,由於SQL Server的鎖管理器會消耗大量的內存,若是你對你的查詢和索引設計不深思熟慮的話。日誌

小結

在SQL Server裏鎖升級基本是個噩夢。你如何才能從表裏刪除5000行記錄而不產生鎖升級?你能夠臨時禁用鎖升級,但這裏你要很是仔細。另一個方法(我推薦的)是讓你的DELETE/UPDATE語句在一個循環裏,做爲不一樣,獨立的事務:DELETE/UPDATE少於5000行記錄,這樣的話你能夠阻止鎖升級。這樣作的好處,你龐大的事務會分解爲多個小事務,但也會讓你的事務日誌更多,帶來自動增加問題。

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2014/02/25/lock-escalations/

相關文章
相關標籤/搜索