鎖是關聯式資料庫為保證資料完整性、防止並發問題(髒讀、不可重複讀、幻讀、更新丟失)而實施的同步機制。SQL Server 動態管理鎖,但理解其運作有助於調優與故障排查。
| 模式 | 縮寫 | 用途 |
|---|---|---|
| 共享鎖 | S | 唯讀操作(SELECT),相容 S/U,不相容 X。 |
| 更新鎖 | U | 可更新資源的預備鎖,防止死鎖,相容 S,不相容 U/X。 |
| 排他鎖 | X | 資料修改(INSERT/UPDATE/DELETE),不相容任何其他鎖。 |
| 意向鎖 | IS/IX/SIX | 建立鎖層級,表示下層將加 S/X 鎖。 |
| 結構描述鎖 | Sch-M/Sch-S | DDL 操作(Sch-M)與查詢編譯(Sch-S)互斥。 |
| 大量更新鎖 | BU | BULK INSERT 配合 TABLOCK 時使用。 |
| 鍵範圍鎖 | RangeS/RangeX | 可序列化隔離級保護範圍查詢,防止幻讀。 |
| 資源 | 說明 |
|---|---|
| KEY | 索引鍵值(範圍鎖用) |
| PAGE | 8KB 頁(資料/索引頁) |
| EXTENT | 8 頁組成的區塊 |
| HoBT | Heap 或 B-Tree(無叢集索引表的資料/索引) |
| TABLE | 整張表(含所有索引) |
| FILE | 資料庫檔案 |
| RID | Heap 中單一資料列識別碼 |
| DATABASE | 整個資料庫 |
| APPLICATION | 應用自定義資源(sp_getapplock) |
| METADATA | 系統目錄中繼資料 |
| ALLOCATION_UNIT | 配置單元 |
粒度權衡:細粒度(ROW/KEY)並發高但記憶體消耗大;粗粒度(TABLE)記憶體少但並發低。SQL Server 會自動升級鎖。
觸發條件:
升級將細粒度鎖(ROW/PAGE)轉為粗粒度(TABLE),釋放記憶體但降低並發。SQL Server 2008 引入表選項控制升級行為:
ALTER TABLE ... SET (LOCK_ESCALATION = ...))| 選項 | 行為 |
|---|---|
| TABLE (預設) | 表級升級,無論是否分區。 |
| AUTO | 分區表→分區級升級(HoBT);非分區表→表級升級。 |
| DISABLE | 停用表級升級。例外:TABLOCK 提示或可序列化隔離級下的 Heap 仍可能產生表鎖。 |
-- 設為分區級升級
ALTER TABLE Person.Address SET (LOCK_ESCALATION = AUTO);
SELECT lock_escalation, lock_escalation_desc FROM sys.tables WHERE name = 'Address';
-- 結果: 2, AUTO
-- 停用升級
ALTER TABLE Person.Address SET (LOCK_ESCALATION = DISABLE);
SELECT lock_escalation, lock_escalation_desc FROM sys.tables WHERE name = 'Address';
-- 結果: 1, DISABLE
sys.dm_tran_locks 監視鎖活動-- 會話 1:加排他表鎖
BEGIN TRAN
SELECT * FROM Production.ProductDocument WITH (TABLOCKX);
-- 會話 2:查詢當前鎖
SELECT request_session_id AS sessionid,
resource_type AS type,
resource_database_id AS dbid,
OBJECT_NAME(resource_associated_entity_id, resource_database_id) AS objectname,
request_mode AS rmode,
request_status AS rstatus
FROM sys.dm_tran_locks
WHERE resource_type IN ('DATABASE', 'OBJECT');
結果會顯示 ProductDocument 上的 X 鎖(GRANT)。
resource_type = OBJECT → resource_associated_entity_id 為 object_id,可用 sys.objects 查名稱。resource_type = ALLOCATION_UNIT → 該 ID 為 allocation_unit_id,需 join sys.allocation_units → sys.partitions 取 object_id。resource_type = KEY/PAGE/RID/HOBT → 該 ID 為 hobt_id,直接查 sys.partitions 取 object_id。resource_type = DATABASE/EXTENT/APPLICATION/METADATA → 該 ID 為 0。LOCK_ESCALATION = AUTO 將升級限制在分區層級。READ COMMITTED SNAPSHOT (RCSI) 或快照隔離,以行版本替代共享鎖。