/* */ Deadlocks and blocking in SQL Server |SQL Works

Tuesday, October 16, 2012

Deadlocks and blocking in SQL Server

As a DBA you are probably faced with the issue of users blocking each other's queries in your databases and potentially deadlocks between queries as well. Here is how I identify and resolve those issues. This method is simple and quick and probably all you need for most situations, there are much more elaborate ways of tracing and troubleshooting these, but for user queries and in-the-moment identification of issues, this way almost always works.

First, the difference between blocking or contention and a deadlock in simple terms, blocking is when one spid or query blocks another from using a resource or placing the lock it needs on said resource, and a deadlock is when two users or queries each holds a resource the other needs next and neither will let go, so the engine chooses a 'victim' and ends the standoff by killing one query. Again this is really simplified but its all you need to know to get started. Now how do we identify when each scenario is happening?

Lets set up some test data first:

/* Created: October 2012
   Author: SQLWorks Consulting blog
   URL: http://www.SQLWorks.blogspot.com

CREATE TABLE [dbo].[EmployeePerf](
[EmployeeID] [int] NOT NULL,
[EmployeeName] [varchar](25) NOT NULL,
[Sales] [int] NULL,
[TimePeriod] [int] NULL

-- test data
INSERT INTO dbo.EmployeePerf VALUES(1,'Tony',100,'1')
INSERT INTO dbo.EmployeePerf VALUES(1,'Tony',300,'2')
INSERT INTO dbo.EmployeePerf VALUES(1,'Tony',200,'3')
INSERT INTO dbo.EmployeePerf VALUES(1,'Tony',150,'4')  

This is just a table I had set up already on my server to test something else, so I am reusing it, the contents of the table are not really relevant to the discussion on database contention.

So now we need two queries, one to block the other for our testing:
-- Looping update to create a lock for demonstration
DECLARE @sales INT = 100
@sales 500000
SET sales @sales
WHERE Employeename 'tony' AND timeperiod 1

SET @sales @sales +1


and in a second window, you will run this concurrently and it will be blocked by the first:

-- select statement that will be blocked by above update statement for demonstration
-- Run this in a different query window!

FROM dbo.EmployeePerf

So now that we have blocking occurring, what do we do about it? Well first I always like to know who is blocking who, and the commands being attempted by each party. To find that out there are several methods, here is the one I use in SQL Server 2012:

DECLARE @sql_handle TABLE(blockee_spid INTblockee_cmd VARCHAR(MAX), blocker_spid INTblocker_cmd VARCHAR(MAX))INSERT INTO @sql_handle
SELECT sp1.spid AS blockee_spidblockee_cmd.TEXT AS blockee_cmdsp1.blocked AS blocker_spidblocker_cmd.TEXT AS blocker_cmd
       FROM sys.sysprocesses sp1
           JOIN sys.sysprocesses sp2
           ON sp1.blocked sp2.spid
       CROSS APPLY sys.dm_exec_sql_text((sp1.sql_handle)) AS blockee_cmd --updated
       CROSS APPLY sys.dm_exec_sql_text((sp2.sql_handle)) AS blocker_cmd --updated
       WHERE sp1.blocked <> 0

SELECT *FROM @sql_handle

This uses the sys.sysprocesses system view to see what is blocking and being blocked and then calls the fn_get_sql function for each to determine what code is being run. If you run the two queries above, then run this script in a third window you will get the below results:

As you can see, it provides the spid of the blocker and blockee, as well as the commands being run by each. There is so much more to delve into on this subject but this will get you moving, and give you a quick way to answer the recurring user question we all love "Who is blocking my query?!?!"

-- EDIT - updated with suggestions from @AaronBertrand of SQLBlog.com
-- changed fn_get_sql to sys.dm_exec_sql_text

-- EDIT - changed formatting of code so comment lines didn't run over code
  -- EDIT - again fixed weird formatting in the code snippets

coming tomorrow, Part 2 - new version using dm views

If you found this useful, click the +1 or Twitter buttons to share -- thanks