Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 SQL Server Development (2000)
 Deadlock problem

Author  Topic 

dbw1276
Starting Member

8 Posts

Posted - 2008-08-30 : 14:56:27
Hey, I hope somebody smarter than me can help out with this.

The app I've been working on has been having a lot of deadlocks lately. I set all the trace flags and ran sqldiag and the problem seems to be a particular index I have on a heavily used table.

When I look at the deadlock graph it looks like I have one process that has an X lock on one part of the index and wants another X lock on another part of the index and a second process that has a S lock on the part of the index the 1st process needs and wants an S lock on the part of the index the 1st process has locked.

The statements involved are pretty straightforward. One is a an update and the other is a simple select (there are no joins).

The dead-lock graph is shown below. First off, am I reading it right? Second, what can I do to prevent this kind of deadlock? Thanks in advance for the help!

2008-08-30 14:25:54.66 spid4 Starting deadlock search 1

2008-08-30 14:25:54.66 spid4 Target Resource Owner:
2008-08-30 14:25:54.66 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80
2008-08-30 14:25:54.66 spid4 Node:1 ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80
2008-08-30 14:25:54.66 spid4 Node:2 ResType:LockOwner Stype:'OR' Mode: X SPID:62 ECID:0 Ec:(0x46343580) Value:0x42bc9a60
2008-08-30 14:25:54.66 spid4 Cycle: ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4 Deadlock cycle was encountered .... verifying cycle
2008-08-30 14:25:54.66 spid4 Node:1 ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80 Cost:(0/0)
2008-08-30 14:25:54.66 spid4 Node:2 ResType:LockOwner Stype:'OR' Mode: X SPID:62 ECID:0 Ec:(0x46343580) Value:0x42bc9a60 Cost:(0/E3C)
2008-08-30 14:25:54.66 spid4 Cycle: ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80 Cost:(0/0)
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4
Deadlock encountered .... Printing deadlock information
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4 Wait-for graph
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4 Node:1
2008-08-30 14:25:54.66 spid4 KEY: 9:107303592:2 (ad0254992921) CleanCnt:1 Mode: X Flags: 0x0
2008-08-30 14:25:54.66 spid4 Grant List 0::
2008-08-30 14:25:54.66 spid4 Owner:0x45166c00 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:62 ECID:0
2008-08-30 14:25:54.66 spid4 SPID: 62 ECID: 0 Statement Type: UPDATE Line #: 88
2008-08-30 14:25:54.66 spid4 Input Buf: RPC Event: dbo.uspFacilitySchedulerUpdateAssign;1
2008-08-30 14:25:54.66 spid4 Requested By:
2008-08-30 14:25:54.66 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80 Cost:(0/0)
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4 Node:2
2008-08-30 14:25:54.66 spid4 KEY: 9:107303592:2 (ad02887b27c2) CleanCnt:1 Mode: S Flags: 0x0
2008-08-30 14:25:54.66 spid4 Grant List 0::
2008-08-30 14:25:54.66 spid4 Owner:0x45151dc0 Mode: S Flg:0x0 Ref:0 Life:00000001 SPID:60 ECID:0
2008-08-30 14:25:54.66 spid4 SPID: 60 ECID: 0 Statement Type: SELECT Line #: 8
2008-08-30 14:25:54.66 spid4 Input Buf: RPC Event: dbo.uspFacilitySchedulerGetAssignBySchedule;1
2008-08-30 14:25:54.66 spid4 Requested By:
2008-08-30 14:25:54.66 spid4 ResType:LockOwner Stype:'OR' Mode: X SPID:62 ECID:0 Ec:(0x46343580) Value:0x42bc9a60 Cost:(0/E3C)
2008-08-30 14:25:54.66 spid4 Victim Resource Owner:
2008-08-30 14:25:54.66 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:60 ECID:0 Ec:(0x45D31580) Value:0x42bc0e80 Cost:(0/0)
2008-08-30 14:25:54.66 spid4
2008-08-30 14:25:54.66 spid4 End deadlock search 1 ... a deadlock was found.
2008-08-30 14:25:54.66 spid4 ----------------------------------

sodeep
Master Smack Fu Yak Hacker

7174 Posts

Posted - 2008-08-31 : 00:19:32
You need to carefully read this article:

http://blogs.msdn.com/bartd/archive/2006/09/25/770928.aspx
Go to Top of Page

GilaMonster
Master Smack Fu Yak Hacker

4507 Posts

Posted - 2008-08-31 : 05:35:21
Can you show us us the update that''s on line 88 of uspFacilitySchedulerUpdateAssign and the select that's on line 8 of uspFacilitySchedulerGetAssignBySchedule. Also the table structure and indexes would be very useful.

--
Gail Shaw
SQL Server MVP
Go to Top of Page

dbw1276
Starting Member

8 Posts

Posted - 2008-08-31 : 09:45:11
quote:
Originally posted by sodeep

You need to carefully read this article:

http://blogs.msdn.com/bartd/archive/2006/09/25/770928.aspx



Thanks! I'll check it out.
Go to Top of Page

dbw1276
Starting Member

8 Posts

Posted - 2008-08-31 : 10:06:39
quote:
Originally posted by GilaMonster

Can you show us us the update that''s on line 88 of uspFacilitySchedulerUpdateAssign and the select that's on line 8 of uspFacilitySchedulerGetAssignBySchedule. Also the table structure and indexes would be very useful.

--
Gail Shaw
SQL Server MVP



Thanks Gail!

Here are the update and select statments.


UPDATE dbo.fsAssign SET
fsDeptID = @fsDeptID,
fsStaffID = @fsStaffID,
Charge = @Charge,
UnscheduledAbsence = @UnscheduledAbsence,
fsCancelReasonID = @fsCancelReasonID,
CancelComment = @CancelComment,
OvertimeShift = @OvertimeShift,
CreateDateUTC = @CreateDateUTC,
fsNeedID = @fsNeedID,
fsNeedStatusID = @fsNeedStatusID,
[Date] = @Date,
fsDeptShiftID = @fsDeptShiftID,
StartTime = @StartTime,
EndTime = @EndTime,
fsCountTypeID = @fsCountTypeID,
fsProductiveID = @fsProductiveID,
Approved = @Approved
WHERE
fsAssignID = @fsAssignID
AND version = @version


SELECT
A.fsAssignID,
A.fsDeptID,
A.fsStaffID,
A.Charge,
A.UnscheduledAbsence,
A.fsCancelReasonID,
A.CancelComment,
A.OvertimeShift,
A.CreateDateUTC,
A.version,
A.fsNeedID,
A.fsNeedStatusID,
A.[Date],
A.fsDeptShiftID,
A.StartTime,
A.EndTime,
A.fsCountTypeID,
A.fsProductiveID,
A.ProviderConfirmed,
A.Approved
FROM
dbo.fsAssign A
WHERE
A.fsDeptID = @fsDeptID
AND A.StartTime < @EndTime
AND A.EndTime > @StartTime


Here is the table structure. I can reproduce the deadlock even if I disable the triggers. Also, in case you're wondering, I use a sequential guid for the primary key so the clustered index doesn't have to be reordered on inserts. Thanks again!


CREATE TABLE [dbo].[fsAssign] (
[fsAssignID] uniqueidentifier ROWGUIDCOL NOT NULL ,
[fsDeptID] [int] NOT NULL ,
[fsStaffID] [int] NOT NULL ,
[Charge] [tinyint] NOT NULL ,
[UnscheduledAbsence] [tinyint] NOT NULL ,
[fsCancelReasonID] [int] NULL ,
[CancelComment] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[OvertimeShift] [tinyint] NOT NULL ,
[CreateDateUTC] [datetime] NULL ,
[appChanged] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[userChanged] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[version] [timestamp] NULL ,
[dateChangedUTC] [datetime] NULL ,
[fsNeedID] [uniqueidentifier] NOT NULL ,
[fsNeedStatusID] [tinyint] NOT NULL ,
[fsOrigDeptID] [int] NOT NULL ,
[Date] [smalldatetime] NOT NULL ,
[fsDeptShiftID] [int] NULL ,
[StartTime] [smalldatetime] NOT NULL ,
[EndTime] [smalldatetime] NOT NULL ,
[fsCountTypeID] [tinyint] NOT NULL ,
[fsProductiveID] [tinyint] NOT NULL ,
[ProviderConfirmed] [tinyint] NOT NULL ,
[Approved] [tinyint] NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[fsAssign] WITH NOCHECK ADD
CONSTRAINT [PK_fsAssign_fsAssignID] PRIMARY KEY CLUSTERED
(
[fsAssignID]
) ON [PRIMARY]
GO

CREATE INDEX [DK_fsAssign_fsDeptIDStartTimeEndTime] ON [dbo].[fsAssign]([fsDeptID], [StartTime], [EndTime]) ON [PRIMARY]
GO

CREATE INDEX [DK_fsAssign_fsStaffIDStartTimeEndTimefsDeptID] ON [dbo].[fsAssign]([fsStaffID], [StartTime], [EndTime], [fsDeptID]) ON [PRIMARY]
GO

CREATE INDEX [DK_fsAssign_fsNeedID] ON [dbo].[fsAssign]([fsNeedID]) ON [PRIMARY]
GO

CREATE INDEX [DK_fsAssign_fsOrigDeptIDStartTimeEndTime] ON [dbo].[fsAssign]([fsOrigDeptID], [StartTime], [EndTime]) ON [PRIMARY]
GO

CREATE INDEX [DK_fsAssign_fsDeptShiftIDStartTimeEndTime] ON [dbo].[fsAssign]([fsDeptShiftID], [StartTime], [EndTime]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[fsAssign] ADD
CONSTRAINT [FK_fsAssign_fsCancelReason_fsCancelReasonID] FOREIGN KEY
(
[fsCancelReasonID]
) REFERENCES [dbo].[fsCancelReason] (
[fsCancelReasonID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsCountType_fsCountTypeID] FOREIGN KEY
(
[fsCountTypeID]
) REFERENCES [dbo].[fsCountType] (
[fsCountTypeID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsDept_fsDeptID] FOREIGN KEY
(
[fsDeptID]
) REFERENCES [dbo].[fsDept] (
[fsDeptID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsDept_fsOrigDeptID] FOREIGN KEY
(
[fsOrigDeptID]
) REFERENCES [dbo].[fsDept] (
[fsDeptID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsDeptShift_fsDeptShiftID] FOREIGN KEY
(
[fsDeptShiftID]
) REFERENCES [dbo].[fsDeptShift] (
[fsDeptShiftID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsNeed_fsNeedID] FOREIGN KEY
(
[fsNeedID]
) REFERENCES [dbo].[fsNeed] (
[fsNeedID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsNeedStatus_fsNeedStatusID] FOREIGN KEY
(
[fsNeedStatusID]
) REFERENCES [dbo].[fsNeedStatus] (
[fsNeedStatusID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsProductive_fsProductiveID] FOREIGN KEY
(
[fsProductiveID]
) REFERENCES [dbo].[fsProductive] (
[fsProductiveID]
) NOT FOR REPLICATION ,
CONSTRAINT [FK_fsAssign_fsStaff_fsStaffID] FOREIGN KEY
(
[fsStaffID]
) REFERENCES [dbo].[fsStaff] (
[fsStaffID]
) NOT FOR REPLICATION
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

CREATE TRIGGER trfsAssign_InsUpd ON dbo.fsAssign
FOR INSERT, UPDATE NOT FOR REPLICATION
AS
SET NOCOUNT ON

UPDATE A SET
A.appChanged = dbo.ufnContextApplication(),
A.userChanged = dbo.ufnContextUser(),
A.dateChangedUTC = GETUTCDATE()
FROM
dbo.fsAssign A
INNER JOIN INSERTED I ON
A.fsAssignID = I.fsAssignID

GO

CREATE TRIGGER trfsAssign_Pending_Upd ON [dbo].[fsAssign]
FOR UPDATE
NOT FOR REPLICATION
AS
SET NOCOUNT ON

IF UPDATE([fsNeedStatusID])
BEGIN

UPDATE N SET
N.Pending = 1
FROM
dbo.fsNeed N
INNER JOIN inserted I ON
N.fsNeedID = I.fsNeedID
INNER JOIN deleted D ON
N.fsNeedID = D.fsNeedID
INNER JOIN dbo.fsAssignFromSrx Srx ON
I.fsAssignID = Srx.fsAssignID
WHERE
I.fsNeedStatusID IN (3,4) -- New status is canceled or no-need
AND I.fsNeedStatusID <> D.fsNeedStatusID

END

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

Go to Top of Page

dbw1276
Starting Member

8 Posts

Posted - 2008-08-31 : 11:35:19
quote:
Originally posted by sodeep

You need to carefully read this article:

http://blogs.msdn.com/bartd/archive/2006/09/25/770928.aspx



This is a very good article. Very interesting. Thanks!

So, if I'm reading it right, is this is what is happening?

1. The select statement places an S lock on the nonclustered index key.
2. The select statement needs an S lock on the clustered index key.
3. The update statement places an X lock on the clustered index key.
4. The update statement needs an X lock on the nonclustered index key.

This seems like what is happening, because the select statement is indeed doing a nonclustered index seek followed by a bookmark lookup.

But if this is what is happening, why does the deadlock graph say that both statements have a lock on the nonclustered index? Shouldn't it say that the update statement has a lock on the clustered index and is requesting a lock on the nonclustered index? Am I reading the graph correctly?

Thanks!
Go to Top of Page

GilaMonster
Master Smack Fu Yak Hacker

4507 Posts

Posted - 2008-08-31 : 12:37:45
I'd guess they're both taking locks on parts of the NC index and then trying to get locks on other parts (or on the cluster).
Is the select using the default isolation level (read committed)? Is it part of a larger explicit transaction?

Is this 2000 or 2005?

Edit: How many rows in the table, how many affected by the update, how many affected by the select?

--
Gail Shaw
SQL Server MVP
Go to Top of Page

dbw1276
Starting Member

8 Posts

Posted - 2008-08-31 : 12:55:33
quote:
Originally posted by GilaMonster

I'd guess they're both taking locks on parts of the NC index and then trying to get locks on other parts (or on the cluster).

--
Gail Shaw
SQL Server MVP



That was what was confusing me. The update statement only updates one row so I couldn't figure out why it would be trying to request more than one key range. Sodeep's explanation makes sense to me in theory, but the deadlock graph doesn't seem to match that explantaion -- unless I'm reading it wrong.

The update statement runs within an explicit transaction. The isolation level is read committed. The select statment does not run within an explicit transaction.

The database is 2000 SP3.

The update statement updates 1 row.

The select statement typically fetches between 50 & 100 rows.

There are about 900,000 rows in the table.

Go to Top of Page
   

- Advertisement -