當共享資料結構的存取極為頻繁且持有時間極短時,閂鎖的上下文切換開銷會成為效能瓶頸。SQL Server 引入自旋鎖 作為更輕量的同步原語:執行緒在 CPU 上主動「自旋」等待,避免進入排程器的 SUSPENDED 狀態,消除上下文切換成本。本文剖析自旋鎖設計動機、指數退避機制、核心 DMV 監控指標,並實戰示範如何用擴充事件定位競爭熱點。
SQLOS 執行緒狀態機包含 RUNNING、SUSPENDED、RUNNABLE 三態。閂鎖無法取得時,查詢進入 SUSPENDED 等待,後轉 RUNNABLE 排隊,最後搶到 CPU 回 RUNNING 取得閂鎖。這對「熱」資料結構(如鎖管理器雜湊表)來說,頻繁的狀態轉換與上下文切換會嚴重拖垮效能。

自旋鎖解決方案:執行緒在 RUNNING 狀態下持續檢查(自旋)鎖是否可用,不放棄 CPU。優點是零上下文切換;缺點是佔用 CPU 週期。為平衡兩者,SQL Server 2008 R2+ 實作指數退避:自旋一段時間後主動睡眠,睡眠間隔呈指數增長,降低 CPU 壓力。
SQL Server 2014 實作 262 種自旋鎖。關鍵欄位:
LOCK_HASH、SOS_SCHEDULER)步驟 1:查詢 LOCK_HASH 的 type 值:
SELECT * FROM sys.dm_xe_map_values WHERE name = 'spinlock_types' AND map_value = 'LOCK_HASH'
步驟 2:建立擴充事件 Session,針對特定 type 捕獲 sqlos.spinlock_backoff 並收集呼叫堆疊:
CREATE EVENT SESSION SpinlockContention ON SERVER
ADD EVENT sqlos.spinlock_backoff (
ACTION (package0.callstack)
WHERE ([type] = 129) -- 上一步查到的值
)
ADD TARGET package0.histogram (SET source = 'package0.callstack', source_type = 1)
GO
步驟 3:分析直方圖目標輸出的呼叫堆疊,定位高 backoff 的程式碼路徑。範例堆疊顯示競爭發生於 XactWorkspaceImp::GetSharedDBLockFromLockManager,即嘗試取得資料庫共享鎖時在鎖管理器雜湊桶上競爭 LOCK_HASH 自旋鎖。
sqldk.dll!XeSosPkg::spinlock_backoff::Publish+0x138
sqlmin.dll!Spinlock<129,7,1>::SpinToAcquireWithExponentialBackoff+0x169
sqlmin.dll!lck_lockInternal+0x841
sqlmin.dll!XactWorkspaceImp::GetSharedDBLockFromLockManager+0x18d
...
亦可啟用追蹤旗標 3656 協助解析堆疊符號。
自旋鎖是 SQL Server 極致效能優化的產物。監控 backoffs 指標、善用擴充事件捕獲堆疊,是診斷「CPU 高但無實際工作」這類疑難雜症的關鍵技能。