<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文屬於基礎知識的回顧,在日常技術交流和日常工作中經常發現有些同事瞭解關於資料庫事務的基本知識,會看SQL語句的執行計劃,也知道資料庫有X鎖、U鎖和S鎖等各種鎖,但是對於這些鎖在資料庫事務執行期間是如何工作?為何這樣配合才能完成資料庫事務?資料庫是如何對於各種資源加鎖的?等等這類的問題不太瞭解,那麼對於事務的執行肯定不會有深刻的認識。
這類知識雖然從網上搜尋可以找到很多,但是大多內容重複,並且只注重理論知識而沒有實踐路徑。就好比池塘中的青蓮只可遠觀而無法靠近仔細觀察,猶如霧裡看花水中望月,對於其真實原理總是似懂非懂。
紙上得來終覺淺,絕知此事要躬行,只有親自動手進行分析才能對這些問題有深入的認識,因此本文計劃從資料庫的基礎知識入手,以詳細的實踐分析步驟引導認識資料庫事務的執行過程,以期讀者可以對於事務有更加深刻的理解。
資料庫引擎使用不同的鎖模式鎖定資源,通過不同鎖的組合使用達到不同的資料庫事務隔離級別。
鎖模式 | 編號 | 效果說明 |
---|---|---|
共用鎖 | 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語句到底是如何應用這些概念呢?
首先通過建表指令碼建立一個資料庫表:
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 |
查詢結果欄位說明:
目前select查詢持有的鎖:
目前的事務執行過程中只對於匹配到的資料行進行了鎖定,如果插入刪除語句並未涉及到該資料行就不會受到影響,但是如果涉及到這行資料那肯定需要等S鎖釋放後才能進行。
首先在事務中執行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 |
該事務持有的鎖:
結合上文中對於鎖型別的講解可以很容易理解資料庫增加這些鎖的用意。資料庫層面增加S鎖可以保護當前正在進行的事務的安全,同時針對發生資料變化的資料頁和資料表增加意向排他鎖可以防止其他事務對於資料庫和資料頁進行更高層的修改(比如架構級別或者DDL之類的事務),IX鎖對於IX和IS是可以並存的,因此可以最大限度上支援同一個區域內的其他修改和查詢事務。
首先在資料庫中執行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 |
該事務持有的鎖:
有了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語句調優都有一定幫助(注意不要在生產環境執行這類分析)。
到此這篇關於一文分析SQL Server中事務使用的鎖的文章就介紹到這了,更多相關SQL Server事務鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45