這篇博文簡單介紹一下在SQL Server中一條Insert語句中用到的鎖。session
首先咱們創建一張表Table_1,它有兩列Id(bigint)和Value(varchar),其中Id創建了主鍵。ide
CREATE TABLE [dbo].[Table_2]( [Id] [bigint] NOT NULL, [Value] [nchar](10) NULL, CONSTRAINT [PK_Table_2] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
而後插入兩條數據。函數
insert into dbo.table_2 (id, value) values (1, '1'), (2, '2');
咱們知道,在Transaction中共享鎖在查詢語句結束就釋放了,而排它鎖則在Transaction提交才釋放。咱們能夠利用它來執行一個Insert,不提交Transaction,而後去查看鎖的狀態。注意,本文中查詢窗口配置的Transaction隔離級別是默認值READ COMMITTED。測試
首先執行如下SQL:spa
begin tran t1 insert into dbo.table_2 (id, value) values (3, '3');
而後查看鎖:3d
SELECT resource_type, request_mode, resource_description, request_session_id, request_status, resource_associated_entity_id, DB_NAME(resource_database_id)as resource_database FROM sys.dm_tran_locks WHERE resource_type <> 'DATABASE' ORDER BY request_session_id;
執行結果以下:code
此時,咱們在另一個命令窗口中執行如下查詢語句不會產生阻塞:對象
SELECT * FROM dbo.Table_2 WHERE id=1;
但另外一條卻會產生阻塞:blog
SELECT * FROM dbo.Table_2 WHERE id=3;
來看看第一條SQL產生的鎖。因爲共享鎖會在查詢結束當即釋放,所以咱們加一個HOLDLOCK,讓它在事務結束再釋放:索引
begin tran t2 SELECT * FROM dbo.Table_2 WITH(HOLDLOCK) WHERE id=1;
這是執行完以上語句鎖的狀況:
第二條SQL會產生阻塞,所以能夠直接查詢而後看鎖的狀況:
咱們發現第9行的resource_description和第3行是相同的,這也說明了主鍵的鎖只是鎖住了某一個值而已。
這條SQL也會被Insert阻塞:
SELECT value FROM dbo.Table_2 WHERE value='1'
並且查看當前的鎖能夠發現,Key被鎖的值正是Insert語句的Key值。這裏有兩個疑問:1. 爲何沒用到主鍵列,卻產生了主鍵鎖。2.爲何Insert的數據還未commit,這裏卻會產生這一行主鍵的鎖。
答:1. 咱們查看查詢計劃,能夠看到這條語句是用了彙集索引掃描,至於爲何不是表掃描,請看這裏。 2. 因爲事務隔離級別默認是Read Committed,因此這裏會對已插入但未提交的數據主鍵加一個共享鎖。