動手分析SQL Server中的事務中使用的鎖

来源:https://www.cnblogs.com/zhu-wj/archive/2022/08/22/16612018.html
-Advertisement-
Play Games

本文屬於基礎知識的回顧,在日常技術交流和日常工作中經常發現有些同事瞭解關於資料庫事務的基本知識,會看SQL語句的執行計劃,也知道資料庫有X鎖、U鎖和S鎖等各種鎖,但是對於這些鎖在資料庫事務執行期間是如何工作?為何這樣配合才能完成資料庫事務?資料庫是如何對於各種資源加鎖的?等等這類的問題不太瞭解,那麼... ...


本文屬於基礎知識的回顧,在日常技術交流和日常工作中經常發現有些同事瞭解關於資料庫事務的基本知識,會看SQL語句的執行計劃,也知道資料庫有X鎖、U鎖和S鎖等各種鎖,但是對於這些鎖在資料庫事務執行期間是如何工作?為何這樣配合才能完成資料庫事務?資料庫是如何對於各種資源加鎖的?等等這類的問題不太瞭解,那麼對於事務的執行肯定不會有深刻的認識。

這類知識雖然從網上搜索可以找到很多,但是大多內容重覆,並且只註重理論知識而沒有實踐路徑。就好比池塘中的青蓮只可遠觀而無法靠近仔細觀察,猶如霧裡看花水中望月,對於其真實原理總是似懂非懂。

紙上得來終覺淺,絕知此事要躬行,只有親自動手進行分析才能對這些問題有深入的認識,因此本文計劃從資料庫的基礎知識入手,以詳細的實踐分析步驟引導認識資料庫事務的執行過程,以期讀者可以對於事務有更加深刻的理解。

SQL Server使用的鎖及鎖對象

資料庫引擎使用不同的鎖模式鎖定資源,通過不同鎖的組合使用達到不同的資料庫事務隔離級別。

鎖模式 編號 效果說明
共用鎖 S 共用鎖,通常用於不修改數據也不希望數據被修改的場景
更新鎖 U 用於可更新的資源,防止這類資源在讀取、鎖定以及隨後可能進行的資源更新時出現死鎖
排他鎖 X 用於修改數據的操作,例如insert、update和delete,防止對同一個資源進行多重修改
意向鎖 包括意向共用、意向更新和意向排他三種,用於保護較低級別的鎖並提升性能
架構鎖 用於執行依賴表結構的操作時使用,包括架構修改 (Sch-M) 和架構穩定性 (Sch-S)
大容量更新 BU 在將數據大容量複製到表中且指定了 TABLOCK 提示時使用
鍵範圍 當使用可序列化事務隔離級別時保護查詢讀取的行的範圍。

意向鎖又細分為多種類型:

鎖模式 編號 效果說明
意向共用 IS 保護針對底層資源的共用鎖
意向排他 IX 保護針對底層資源的排他鎖是,IS的超集
共用意向排他 SIX 保護針對低層資源請求或獲取的意向排他鎖以意向共用鎖
意向更新 IU 保護針對底層資源的更新鎖
共用意向更新 SIU S鎖和IU鎖的組合,作為分別獲取並同時具備兩種鎖的組合效果
更新意向排他 UIX U鎖和IX鎖的組合,作為分別獲取並同時具備兩種鎖的組合效果

架構鎖細分為兩種類型:

鎖模式 編號 效果說明
架構修改鎖 Sch-M DDL執行期間使用架構修改鎖,該鎖會阻止對於表的所有訪問
架構穩定鎖 Sch-S 該鎖不會影響S、U以及X鎖的執行,但是會阻止DDL的執行

通常開發人員談到資料庫的鎖的時候習慣說資料庫鎖、表鎖或者行鎖。這種描述通常是從被鎖定資源的角度來談論,通過檢索SQL Server2016的文檔發現資料庫上鎖定更多的資源不只是這三種維度,還有11種類型。

鎖對象 關於鎖對象的說明
AllocUnit 分配單元
Application 應用程式專用的資源
Database 整個資料庫
Extent 一組連續的8個頁
File 資料庫文件
Heap/B-tree 堆或者B樹
Key 索引上的某一行
Metadata 元數據
Object 表、存儲過程、視圖等包括所有的數據和索引
OIB 用於聯機索引構建時的鎖
Page 資料庫上8KB頁
RID 堆上的某一行
RowGroup 列存儲索引行組的時候使用的鎖
Xact 事務的鎖定資源

瞭解了資料庫的鎖及其鎖定對象,那麼日常使用的select、insert和update語句到底是如何應用這些概念呢?

SQL Server執行Select時使用的鎖

首先通過建表腳本創建一個資料庫表:

USE [Test]
GO
/****** Object:  Table [dbo].[UserTable]    Script Date: 2022/6/29 20:08:23 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UserTable](
	[id] [varchar](36) NOT NULL,
	[name] [varchar](256) NULL,
	[code] [varchar](256) NULL,
	[createtime] [datetime] NULL,
	[lastmodifytime] [datetime] NULL,
PRIMARY KEY CLUSTERED 
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

USE [Test]
GO
INSERT [dbo].[UserTable] ([id], [name], [code], [createtime], [lastmodifytime]) VALUES (N'5E4B68B0-71B8-43FB-B6B4-8E9D43A30589', N'test1', N'123456', CAST(N'2022-06-29T18:02:21.517' AS DateTime), CAST(N'2022-06-29T18:02:21.517' AS DateTime))
GO


由於Select語句在SQL Server的預設事務隔離級別(read commited)中執行完成後就會釋放相關的鎖,而非等到事務結束,在這種情況下無法通過sp_lock或者sys.dm_tran_locks視圖觀察select語句執行過程中鎖的執行情況,因此比較方便的辦法是在查詢語句執行之前調整當前會話的事務隔離級別為repeatable read,在這個隔離級別中select語句預設會在事故執行完成後提交,比較方便分析。

在SQL Server Manager Studio的查詢視窗中執行語句:

set transaction isolation level repeatable read

set statistics profile on 

begin tran
select * from usertable where  id='5E4B68B0-71B8-43FB-B6B4-8E9D43A30589'

在前面的事務目前是已經執行未提交的狀態,此時可以通過dm_tran_locks查詢到該語句目前持有的鎖:

select request_session_id,resource_type,request_status,request_mode,resource_description,
case resource_type
when 'Page' then OBJECT_NAME(p.object_id)
when 'object' then OBJECT_NAME(lock.resource_associated_entity_id)
when 'database' then (select name from master..SysDatabases where dbid=resource_database_id)
when 'key' then object_name(p.object_id)
end 
as objectName from sys.dm_tran_locks lock
left join sys.partitions p 
on p.hobt_id=lock.resource_associated_entity_id
order by lock.request_session_id

request_session_id resource_type request_status request_mode resource_description objectName
62 DATABASE GRANT S Test
62 PAGE GRANT IS 0.236111111 UserTable
62 OBJECT GRANT IS UserTable
62 KEY GRANT S (0ee48b5e6942) UserTable

查詢結果欄位說明:

  • request_session_id:會話編號
  • resource_type:被鎖定的資源類型
  • request_status:請求的狀態
  • request_mode:鎖類型
  • resource_description 資源描述情況
  • objectName:對象名稱

目前select查詢持有的鎖:

  1. 通過目前的查詢結果可以看到在DATABASE上加了S鎖(資料庫名為Test);
  2. 在數據所屬的頁上增加了意向共用鎖;
  3. 表上增加了意向共用鎖;
  4. 數據行上增加了共用鎖;

目前的事務執行過程中只對於匹配到的數據行進行了鎖定,如果插入刪除語句並未涉及到該數據行就不會受到影響,但是如果涉及到這行數據那肯定需要等S鎖釋放後才能進行。

SQL Server執行insert時使用的鎖

首先在事務中執行insert語句並且不提交(註意將上個章節中的事務提交):

begin tran

insert into UserTable (id,code,name,createtime,lastmodifytime)
values(newid(),'test2','測試用戶2',getdate(),getdate())

insert的時候預設會有事務,因此主動聲明一個事務並只執行不提交就可以很容易的查到當前會話持有的鎖。

通過dm_tran_locks查詢到該語句目前持有的鎖:

request_session_id resource_type request_status request_mode resource_description objectName
70 DATABASE GRANT S Test
70 PAGE GRANT IX 1:280 UserTable
70 OBJECT GRANT IX UserTable
70 KEY GRANT X (c75ad92ba798) UserTable

該事務持有的鎖:

  1. 資料庫層面的共用鎖;
  2. 數據頁上的意向排他鎖;
  3. 數據表的意向排他鎖;
  4. 數據行的排他鎖;

結合上文中對於鎖類型的講解可以很容易理解資料庫增加這些鎖的用意。資料庫層面增加S鎖可以保護當前正在進行的事務的安全,同時針對發生數據變化的數據頁和數據表增加意向排他鎖可以防止其他事務對於資料庫和數據頁進行更高層的修改(比如架構級別或者DDL之類的事務),IX鎖對於IX和IS是可以並存的,因此可以最大限度上支持同一個區域內的其他修改和查詢事務。

SQL Server執行update時使用的鎖

首先在資料庫中執行update語句而不提交(註意將上個章節中的事務提交或者回滾):

begin tran 

update UserTable set lastmodifytime=GETDATE()  where id ='06757850-68D6-416C-B3D1-FD3B29BAD4BB'

通過dm_tran_locks查詢到該語句目前持有的鎖:

request_session_id resource_type request_status request_mode resource_description objectName
52 DATABASE GRANT S Test
52 PAGE GRANT IX 1:280 UserTable
52 OBJECT GRANT IX UserTable
52 KEY GRANT X (ead909dc80bf) UserTable

該事務持有的鎖:

  1. 資料庫層面的共用鎖;
  2. 數據頁上的意向排他鎖;
  3. 數據表上面的意向排他鎖;
  4. 數據行的排他鎖;

有了insert的經驗後,理解update語句使用的鎖難度就不大了。其與insert使用的鎖的類型基本一樣,由於本次是使用主鍵進行修改,資料庫可以直接定位到需要進行變更的數據行,因此只需要在對應的行上增加X鎖就可以滿足事務的需要。

日常使用的時候很少直接通過id更新數據,往往基於一些非聚集索引更新數據,在這種情況下資料庫對於鎖的使用會有什麼不一樣呢?首先針對測試的數據表增加兩個索引:

create nonclustered index idx_UserTable_Name on UserTable(name)
create nonclustered index idx_UserTable_LastModifyTime on UserTable(lastmodifytime)

然後將update語句修改為根據name更新數據:

begin tran 

set statistics profile on 

update UserTable set lastmodifytime=GETDATE()  where name like '%test%'

該語句對應的鎖的情況統計:

request_session_id resource_type request_status request_mode resource_description objectName
52 DATABASE GRANT S Test
52 PAGE GRANT IX 1:280 UserTable
52 PAGE GRANT IX 1:368 UserTable
52 KEY GRANT X (ba4eae1b81ad) UserTable
52 KEY GRANT X (500c265deab6) UserTable
52 KEY GRANT X (a1a185fdb4ae) UserTable
52 OBJECT GRANT IX UserTable
52 KEY GRANT X (ff4928fe375a) UserTable
52 KEY GRANT X (0ee48b5e6942) UserTable

可以發現通過非聚集索引更新數據的時候,資料庫需要檢查的內容明顯增加,並且增加IX鎖的數據也多了不少。只看這個表格可能不太好理解,這些key對應的X鎖為什麼要增加,以及是使用的哪個索引呢?為了瞭解更多的信息,上文中查詢事務鎖的語句需要進行一些改動,增加對於索引的關聯查詢:

with indexs 
as (
SELECT  索引名稱 = a.name ,
        表名 = c.name ,
        索引欄位名 = d.name ,
        a.indid
FROM    sysindexes a
        JOIN sysindexkeys b ON a.id = b.id
                               AND a.indid = b.indid
        JOIN sysobjects c ON b.id = c.id
        JOIN syscolumns d ON b.id = d.id
                             AND b.colid = d.colid
WHERE   a.indid NOT IN ( 0, 255 )  
AND   c.name='UserTable' --查指定表 
)
select request_session_id,resource_type,request_status,request_mode,resource_description,
case resource_type
when 'Page' then OBJECT_NAME(p.object_id)
when 'object' then OBJECT_NAME(lock.resource_associated_entity_id)
when 'database' then (select name from master..SysDatabases where dbid=resource_database_id)
when 'key' then object_name(p.object_id)
end 
as objectName,index_id,i.索引名稱 from sys.dm_tran_locks lock
left join sys.partitions p on p.hobt_id=lock.resource_associated_entity_id
left join indexs  i on i.indid=index_id
order by lock.request_session_id

通過關聯查詢索引信息,得到了更豐富的內容:

request_session_id resource_type request_status request_mode resource_description objectName index_id 索引名稱
52 DATABASE GRANT S Test NULL NULL
52 PAGE GRANT IX 1:280 UserTable 1 PK__Test1__3213E83F133024F3
52 PAGE GRANT IX 1:368 UserTable 4 idx_UserTable_LastModifyTime
52 KEY GRANT X (ba4eae1b81ad) UserTable 4 idx_UserTable_LastModifyTime
52 KEY GRANT X (500c265deab6) UserTable 4 idx_UserTable_LastModifyTime
52 KEY GRANT X (a1a185fdb4ae) UserTable 1 PK__Test1__3213E83F133024F3
52 OBJECT GRANT IX UserTable NULL NULL
52 KEY GRANT X (ff4928fe375a) UserTable 4 idx_UserTable_LastModifyTime
52 KEY GRANT X (0ee48b5e6942) UserTable 1 PK__Test1__3213E83F133024F3
52 KEY GRANT X (150ba0b85c41) UserTable 4 idx_UserTable_LastModifyTime

從上表中可以看出在更新數據的時候,由於涉及到多行的非聚集索引上面的數據,因此對於該索引涉及到的數據行都增加了X鎖,涉及到的數據頁也比之前更多了。類型為X鎖,同時索引名稱為PK__Test1__3213E83F133024F3的有兩行,因為本次事務匹配到了兩行數據;類型為X鎖,同時索引名稱為idx_UserTable_LastModifyTime的一共有四行。為什麼是四行呢?因為有兩個舊的數據需要刪除,同時新增了兩個新的數據,所以是四行。其他的非聚集索引的數據並沒有修改,所以本次不需要申請X鎖。

總結

資料庫中的各種事務隔離級別都是通過對於不同鎖的綜合運用實現的。對於鎖的認識可以從兩個角度進行:鎖模式和鎖對象。哪怕是一個簡單的select語句都會有預設的某種鎖以保護數據的正確性。需要註意不同的數據組合情況、不同的事務隔離級別下SQL語句的執行過程可能是不一樣的,因此其使用的鎖也會千變萬化,本文所列舉的只是一些很簡單的情況,但是規則類似,分析路徑也是基本一致的,有興趣的可以自己嘗試下日常工作中語句的執行過程中使用的鎖,這對於理解資料庫工作原理,有針對性的對於SQL語句調優都有一定幫助(註意不要在生產環境執行這類分析)。

參考文檔

  1. SQL Server, Locks object
  2. 事務鎖定和行版本控制指南
  3. Microsoft SQL Server企業級平臺管理實踐

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是IAR下調試信息輸出機制之硬體UART外設。 在嵌入式世界里,輸出列印信息是一種非常常用的輔助調試手段,藉助列印信息,我們可以比較容易地定位和分析程式問題。在嵌入式應用設計里實現列印信息輸出的方式有很多,本系列將以 IAR 環境為例逐一 ...
  • Visio 繪圖的工具哪款好用?推薦使用VSDX Annotator Mac版,一款用於在 Mac 上操作 MS Visio 繪圖的工具。它提供了廣泛的註釋可能性,以及在多平臺環境中共用可視文檔。 詳情:VSDX Annotator for mac(Visio 繪圖工具) 在 Mac 上查看、編輯和 ...
  • 想要一款觸控板增強工具?現為大家帶來了這款滑鼠增強工具Smooze Pro!Smooze Pro Mac版幫助您平滑滾動,以及向任何應用程式添加自定義滑鼠按鈕操作和滑鼠手勢。功能強大,操作簡單。 詳情:Smooze Pro for Mac(滑鼠增強工具) 功能 一旦啟動,Smooze 將添加一個帶有 ...
  • 1.巴西 ANATEL 就有線充電介面USB Type-C 標準開展公眾咨詢 巴西ANATEL已啟動第 45 號公眾咨詢,該提案定義了強制性技術要求,以評估手機中有線充電介面與 USB Type-C 標準的合規性。增加不同設備(包括手機)使用協同充電介面的要求,該介面應集成基於USB C型標準的協調 ...
  • 1.模里西斯ICTA啟用6 GHz頻段! 2022年8月4日,模里西斯信息和通信技術管理局 (ICTA) 發佈了《關於在 5945-6425 MHz頻率範圍內為寬頻無線接入服務分配額外頻譜的決定》,允許5945-6425 MHz頻段可用於以下設備: • 僅限室內使用的室內低功率 (ILP) 設備。不 ...
  • Camera Raw是Photoshop和其他 Creative Suite應用程式的有用插件,可以訪問由各種專業和中檔數位相機生成的“原始”圖像格式。通過 Camera Raw,您可以對來自各種不同相機的原始圖像進行增強,以及將圖像導入至各個應用程式。 詳情:Camera Raw 功能介紹 一步式 ...
  • 當然,目前 StoneDB 的社區建設還正處於初啟階段,我們堅信,開源項目的成長,最終還是要靠社區用戶一起來共創,因此,StoneDB 開源社區非常重視社區用戶的聲音,在 7 月份,我們從各個渠道里收集到了用戶的反饋,這裡做一個彙總,同步給各位關註 StoneDB 的開發者們,無論您是在校學生還是公 ...
  • 一、直播介紹 前幾期,我們為大家分享了Taier基本介紹、控制台、Web前端架構及數據開發介紹,本期我們為大家分享Taier任務調度介紹。 本次直播我們將從Taier的任務調度實例生成、調度及提交等方面為大家進行介紹,通過本次分享,希望大家能對Taier有更進一步的瞭解。 二、直播主題 Taier任 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...