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
BEGIN TRAN
WHILE @sales < 500000
BEGIN
UPDATE [dbo].[EmployeePerf]
SET sales = @sales
WHERE Employeename = 'tony' AND timeperiod = 1
SET @sales = @sales +1
CONTINUE
END
COMMIT TRAN
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!
SELECT *
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 INT, blockee_cmd VARCHAR(MAX), blocker_spid INT, blocker_cmd VARCHAR(MAX))INSERT INTO @sql_handle
SELECT sp1.spid AS blockee_spid, blockee_cmd.TEXT AS blockee_cmd, sp1.blocked AS blocker_spid, blocker_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
If you found this useful, click the +1 or Twitter buttons to share -- thanks
No comments:
Post a Comment