在今天的文章裡我想談下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),
www.cppcns.com 排它的(exclusive), 更新(update),排它的(exclusive),
意向的(intension) 銷毀(destroy)
死鎖…… 檢測並解決(detection&resolution) 通過嚴謹**來避免
保持在…… 鎖管理器的雜湊表(hashtable) 保護的資料結構(protected data structure)
從表裡可以看到,閂鎖支援更細粒度保持(keep)和銷毀(destroy)模式。保持閂鎖主要用來引用計數,例如當你想知道在指定閂鎖上有多少其它閂鎖在等待。銷毀閂鎖是最有限制的乙個(它甚至會阻塞保持閂鎖),當閂鎖被銷毀時會用到,例如當惰性寫入器(lazy writer)想要釋放記憶體中的頁時。我們先介紹下各種閂鎖模式,然後說下各個閂鎖模式的相容性。
nl(空閂鎖):
內部未使用
kp(保持閂鎖):
可以由多個任務同時持有
只被乙個dt模式的閂鎖阻塞
sh(共享閂鎖):
&nb程式設計客棧sp; 讀取資料頁的時候使用
可以由多個任務同事持有
阻塞ex模式和dt模式的閂鎖
up(更新閂鎖):
寫入系統分配頁面和tempdb的行版本化頁面時使用
乙個這種模式的閂鎖只能被乙個單獨的任務持有
ex(排它閂鎖):
寫入資料頁的時候使用
乙個這種模式的閂鎖只能被乙個單獨的任務持有
dt(銷毀閂鎖):
很少使用
乙個這種模式的閂鎖只能被乙個單獨的任務持有
在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 檢視下這些閂鎖型別的等待。
複製** 代程式設計客棧碼如下:
select * from sys.dm_os_wait_stats where wait_type like 'pageiolatch_%'
使用這些閂鎖,sql server保證那些頁不會併發多次讀入快取池,那些頁也不會從快取池忽略,在那些頁需要被查詢訪問的時候。
除這些i/o閂鎖外,sql server也支援所謂的快取區閂鎖(buffer latches),它用來保護緩衝池裡頁不會被併發執行的執行緒影響。這些閂鎖,sql server使用它們來阻止記憶體中的丟失更新(lost updates)。沒有這類閂鎖,在快取池裡會有併發的讀寫頁,它們會引發主記憶體裡頁的損壞。對於這些快取閂鎖,sql server在統計資訊裡以pagelatch_為字首出現。你可以在dmv sys.dm_os_wait_stats 檢視下這些閂鎖型別的等待。這裡最重要的是你涉及了主記憶體的競爭,因為他們的等待型別名稱裡不包含io字樣。
複製** **如下:
select * from sys.dm_os_wait_stats where wait_type like 'pagelatch_%'
最後sql server內部使用所謂的非快取區閂鎖(non-buffer latches)來保護除緩衝池外的共享資料結構。對於這些非快取閂鎖,sql server在統計資訊裡以latch_為字首出現。你可以在dmv sys.dm_os_wait_stats 檢視下這些閂鎖型別的等待。
複製** **如下:
select * from sys.dm_os_wait_stats where wait_type like 'latch_%'
但在這個dmv裡這些對於非快取區閂鎖的等待只是sql server內部使用的各個閂鎖的概況資訊,你可以在單獨的dmv sys.dm_os_latch_stats找到更詳細的資訊。
複製** **如下:
select * from sys.dm_os_latch_stats
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 7.0裡引入,同時微軟首次引入了行級別鎖 row level locking 對於行級別鎖引入閂鎖的概念是非常重要的,不然的話在記憶體中會出現丟失更新 lost updates 的現象。如我所說的,閂鎖是儲存引擎使用的輕量級同步物件,是sql server用來保護記憶...
SQL Server裡簡單引數化的痛苦
在今天的文章裡,我想談下對於即席sql語句 ad hoc sql statements sql server使用的簡單引數化 parameterization 的一些特性和 首先,如果你的sql語句包含這些,簡單引數化不會發生 一般來說,如果你處理所謂的安全執行計畫 safe execution p...
SQL Server裡簡單引數化的痛苦
原文 sql server裡簡單引數化的痛苦 在今天的文章裡,我想談下對於即席sql語句 ad hoc sql statements sql server使用的簡單引數化 parameterization 的一些特性和 首先,如果你的sql語句包含這些,簡單引數化不會發生 一般來說,如果你處理所謂的...