[SQL]建立Index来避免DeadLock

大家都知道Scan去找数据是不好的,但您知道这样的SQL在执行中,会拿了多少的Lock吗?
我们可以从SQL Profiler中的Lock:Acquired及Lock:Released来观察哦!


最近在论谈上看到“两个事务 更新一张堆表 遇到奇怪的死锁问题”。

这让我想到之前在TechDay上到强哥说的,你认为SQL Update时,一定是Row Lock吗?

当某个Table没有建立任何的Index,要更新数据就是要一笔笔的找,解法就是建立index把数据打散!

强哥的测试Script如下(使用TSQL2012),


--SESSION I

USE TSQL2012
GO
IF OBJECT_ID('employees') IS NOT NULL
    DROP TABLE employees
GO

SELECT  *
INTO    employees
FROM    [HR].[Employees]
GO

SELECT  *
FROM    [dbo].[employees]

BEGIN TRANSACTION 
UPDATE  [dbo].[employees]
SET     [firstname] = [firstname] + '@'
WHERE   empid = 1

 
SELECT  *
FROM    [dbo].[employees]
WHERE   empid = 1

--ROLLBACK

--SESSION 2
SELECT  *
FROM    [dbo].[employees]
WHERE   empid = 2
--WHERE lastname='Funk'

--solution
DROP INDEX IDX_SPLIT ON [dbo].[employees]

CREATE CLUSTERED INDEX IDX_SPLIT ON [dbo].[employees]
([empid] DESC,[lastname] ASC  )
 GO

那这个问题的状况也类似,Script如下,


USE tempdb
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table1](
 [A] [nvarchar](10) NULL,
 [B] [nvarchar](10) NOT NULL,
 [C] [nvarchar](10) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b1', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa2', N'b3', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b4', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b5', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b2', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b6', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b7', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b8', N'11')
INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b9', N'11')

开2个查询窗口,先执行第1个查询,再切到第2个查询执行,


--查询1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRAN
UPDATE  table1
SET     A = 'aa1'
WHERE   B = 'b3'
EXEC sp_lock @@spid
 
WAITFOR  DELAY '00:00:10'

UPDATE  table1
SET     A = 'aa2'
WHERE   B = 'b8'
EXEC sp_lock @@spid
WAITFOR  DELAY '00:00:10'
COMMIT TRAN

--查询2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRAN
UPDATE  table1
SET     A = 'aa3'
WHERE   B = 'b1'
 
EXEC sp_lock @@spid
COMMIT TRAN

等一下子后,查询2就会被牺牲掉,如下,

Msg 1205, Level 13, State 45, Line 3
Transaction (Process ID 117) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Msg 1205, Level 13, State 45, Line 3
事务 (处理序识别码 117) 在 锁定 资源上被另一个处理序锁死并已被选择作为死结的牺牲者。请重新执行该事务。

DeadLock的图如下,

image

sp_lock的资讯如下,

image

Update时,因为没有任何的index,所以是用Scan的方式,如下,

image

所以,它会一笔笔地找,先取得Update Lock,不符合就Rease Update Lock,然后再取下一笔,有符合的,就取得  Exclusive Lock 。

而第2个查询,会等待是因为要取得 Update Lock ,而该笔数据被查询1取得了Exclusive Lock了!

image

So...这种状况就是建立Index让它可以马上找到,而不需用Scan的方式去找,而造成Lock的等待,


CREATE NONCLUSTERED INDEX nidx_table1_B
ON table1(B);

而建立了index后,可以发现,不会逐笔扫,而会直接针对该笔数据进行 Update 及 Exclusive Lock 然后修改完数据后,Release Lock! 如下,

image

参考数据

Using a Clustered Index to Solve a SQL Server Deadlock Issue

两个事务 更新一张堆表 遇到奇怪的死锁问题

原文:大专栏  [SQL]建立Index来避免DeadLock


猜你喜欢

转载自www.cnblogs.com/chinatrump/p/11458255.html