天天看點

SQL Server裡的闩鎖介紹

在今天的文章裡我想談下SQL Server使用的更進階的,輕量級的同步對象:闩鎖(Latch)。闩鎖是SQL Server存儲引擎使用輕量級同步對象,用來保護多線程通路記憶體内結構。文章的第1部分我會介紹SQL Server裡為什麼需要闩鎖,在第2部分我會給你介紹各個闩鎖類型,還有你如何能對它們進行故障排除。

為什麼我們需要闩鎖?

闩鎖首次在SQL Server 7.0裡引入,同時微軟首次引入了行級别鎖(row-level locking)。對于行級别鎖引入闩鎖的概念是非常重要的,不然的話在記憶體中會出現丢失更新(Lost Updates)的現象。如我所說的,闩鎖是存儲引擎使用的輕量級同步對象,是SQL Server用來保護記憶體結構的。闩鎖隻不過是類似于多線程程式設計裡的所謂的臨界區(Critcal Section)概念。

在傳統并發程式設計裡,臨界區是同時隻能一個線程運作的代碼。闩鎖本身是個臨界區的特殊版本,因為它允許多個并發讀操作。在SQL Server的上下文裡這意味着多個線程可以并發讀取一個共享資料結構,例如記憶體中的頁,但是寫入共享資料結構必須是單個線程進行。

闩鎖是用來協調資料庫裡多個線程實體執行,然而鎖是基于選擇的事務隔離級别,用來邏輯獲得需要的隔離級别。作為開發者或DBA的你,你可以用不同方式影響鎖——例如通過SQL Server裡的隔離級别,或者通過各種可用鎖提示。另一方面闩鎖是不能以直接方式控制的。在SQL Server裡沒有闩鎖提示,也沒有可用闩鎖隔離級别。下表是鎖和闩鎖之間的比較:

                 鎖(Locks)         闩鎖(Latches)

  • 控制……      事務              線程
  • 保護……    資料庫内容          記憶體中資料結構
  • 模式……     共享的(Shared),      保持(Keep),

                 更新(Update),         共享的(Shared),

                 排它的(Exclusive),     更新(Update),排它的(Exclusive),

                 意向的(Intension)      銷毀(Destroy)

  • 死鎖…… 檢測并解決(detection&resolution)  通過嚴謹代碼來避免
  • 保持在……  鎖管理器的哈希表(Hashtable)   保護的資料結構(Protected Data Structure)

從表裡可以看到,闩鎖支援更細粒度保持(Keep)和銷毀(Destroy)模式。保持闩鎖主要用來引用計數,例如當你想知道在指定闩鎖上有多少其它闩鎖在等待。銷毀闩鎖是最有限制的一個(它甚至會阻塞保持闩鎖),當闩鎖被銷毀時會用到,例如當惰性寫入器(Lazy Writer)想要釋放記憶體中的頁時。我們先介紹下各種闩鎖模式,然後說下各個闩鎖模式的相容性。

NL(空闩鎖):

  • 内部
  • 未使用

KP(保持闩鎖):

  • 可以由多個任務同時持有
  • 隻被一個DT模式的闩鎖阻塞

SH(共享闩鎖):

  • 讀取資料頁的時候使用
  • 可以由多個任務同僚持有
  • 阻塞EX模式和DT模式的闩鎖

UP(更新闩鎖):

  • 寫入系統配置設定頁面和tempdb的行版本化頁面時使用
  • 一個這種模式的闩鎖隻能被一個單獨的任務持有

EX(排它闩鎖):

  • 寫入資料頁的時候使用

DT(銷毀闩鎖):

  • 很少使用
SQL Server裡的闩鎖介紹

在SQL Server裡,一緻性不能隻用鎖來獲得。SQL Server還是可以通路沒被鎖管理器保護的共享資料結構,例如頁頭。還有SQL Server基于闩鎖基礎的其他元件也是,有單線程代碼路徑。好了,我們繼續講解SQL Server裡的各種闩鎖類型,還有如何對它們進行故障排除。

闩鎖類型與故障排除

SQL Server區分3個不同闩鎖類别

  • IO闩鎖
  • 緩沖區闩鎖(BUF)
  • 非緩沖區闩鎖(Non-BUF)

我們來詳細看下這3個不同類别。當在緩沖池的頁讀寫操作未完成——即當你讀自/寫入你的存儲子系統時(2者未同步),SQL Server會使用IO闩鎖(I/O Latches)。對于這些I/O闩鎖,SQL Server在統計資訊裡以PAGEIOLATCH_為字首出現。你可以在DMV sys.dm_os_wait_stats 檢視下這些闩鎖類型的等待。

1 SELECT * FROM sys.dm_os_wait_stats WHERE wait_type LIKE 'PAGEIOLATCH_%'      
SQL Server裡的闩鎖介紹

使用這些闩鎖,SQL Server保證那些頁不會并發多次讀入緩存池,那些頁也不會從緩存池忽略,在那些頁需要被查詢通路的時候。

除這些I/O闩鎖外,SQL Server也支援所謂的緩存區闩鎖(Buffer Latches),它用來保護緩沖池裡頁不會被并發運作的線程影響。這些闩鎖,SQL Server使用它們來阻止記憶體中的丢失更新(Lost Updates)。沒有這類闩鎖,在緩存池裡會有并發的讀寫頁,它們會引發主記憶體裡頁的損壞。對于這些緩存闩鎖,SQL Server在統計資訊裡以PAGELATCH_為字首出現。你可以在DMV sys.dm_os_wait_stats 檢視下這些闩鎖類型的等待。這裡最重要的是你涉及了主記憶體的競争,因為他們的等待類型名稱裡不包含IO字樣。

1 SELECT * FROM sys.dm_os_wait_stats WHERE wait_type LIKE 'PAGELATCH_%'      
SQL Server裡的闩鎖介紹

最後SQL Server内部使用所謂的非緩存區闩鎖(Non-Buffer Latches)來保護除緩沖池外的共享資料結構。對于這些非緩存闩鎖,SQL Server在統計資訊裡以LATCH_為字首出現。你可以在DMV sys.dm_os_wait_stats 檢視下這些闩鎖類型的等待。

1 SELECT * FROM sys.dm_os_wait_stats WHERE wait_type LIKE 'LATCH_%'      
SQL Server裡的闩鎖介紹

但在這個DMV裡這些對于非緩存區闩鎖的等待隻是SQL Server内部使用的各個闩鎖的概況資訊,你可以在單獨的DMV sys.dm_os_latch_stats找到更詳細的資訊。

1 SELECT * FROM sys.dm_os_latch_stats      
SQL Server裡的闩鎖介紹

SQL Server 2014内部使用163個闩鎖來同步共享資料結構的通路。其中一個著名的闩鎖是FGCB_ADD_REMOVE,它用來保護檔案組的檔案組控制阻塞( File Group Control Block (FGCB)),在以下特定操作期間:

  • 檔案增長(手動或自動)
  • 增加/删除檔案組檔案
  • 重新計算填充比重(Recalculating proportional fill weightings)
  • 在循環配置設定期間,通過檔案組的檔案回收。

當你看到這個闩鎖排在前列是,你肯定有太多自動增長操作的問題,原因是你資料庫糟糕的預設配置。當查詢嘗試讀/寫保護的資料結構且需要等待一個闩鎖時,查詢就會進入挂起狀态,直到闩鎖可以成功擷取。是以查詢經過的整個查詢生命周期包括運作(RUNNING),挂起(SUSPENDED),可運作(RUNNABLE),最後再次運作(RUNNING)。是以,當查詢長時間把持闩鎖時,強制共享資料結構保護才有意義。那是因為改變查詢狀态也意味着進行Windows系統裡的上下文切換,依據引入的CPU周期是個很昂貴的操作。

是以對于讀寫頻繁或極短時間内的共享資料結構上放上闩鎖沒有意義。在這個情況下,需要的上下文切換會殺死SQL Server的整體性能,它需要花費太多的時間來完成整個查詢生命周期(運作(RUNNING),挂起(SUSPENDED),可運作(RUNNABLE))。那就是是SQL Server引入的所謂自旋鎖(Spinlocks)。鎖管理器就是這樣資料結構的好例子:當鎖定或解鎖資料對象(例如記錄,頁等)時隻需要單個線程通路。但當你檢視sys.dm_os_latch_stats時,你會發現沒有闩鎖保護鎖管理器本身。鎖管理器使用的哈希表裡對應的哈希桶使用自旋鎖來保護——LOCK_HASH自旋鎖。通過鎖管理器執行鎖定和解鎖操作前,必須獲得自旋鎖。但今天我不想再講解自旋鎖了,因為我會在專門的文章裡詳細介紹——耐心期待下吧:)

小結

在這個文章裡,我們探讨了SQL Server裡的闩鎖。如你所見,闩鎖是SQL Server使用的輕量級同步對象,用來儲存記憶體裡的共享資料結構。SQL Server區分3個不同闩鎖類型——IO闩鎖,緩存區闩鎖和非緩存區闩鎖。你也看到了如何使用DMV sys.dm_os_wait_stats 和sys.dm_os_latch_stats進行闩鎖等待的故障排除。

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2014/06/23/introduction-to-latches-in-sql-server/

注:此文章為

WoodyTu

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

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