Troubleshoot Database Concurrency in SQL Server with sp_locks


concurrency can be defined as the number of users that can work at a given
database at the same time while not effecting or interfering with eachother’s
work. The greater the number of users working at the same time the higher concurrency

SQL Server
holds Locks on user data to protect data integrity and this is required in
every database where data gets modified. Just to emphasize, be aware that if a
database is set in a READ_ONLY state SQL Server does not hold locks on data being
read since it cannot be modified and so there is no data that needs to be protected,
therefore SQL Server only marks the objects being accessed with an Intense
Share (IS) lock to prevent a DDL statement such as dropping a table while it’s
data is being read.

Locks on user
data exist at any environment where data gets modified (the database is set in
READ_WRITE state as opposed to the above example) and this is absolutely OK.
SQL Server holds the required locks to protect data integrity and releases the
locks as soon as they are no longer required.

Short terms
locks have no negative impact and they are part of the normal work that SQL
Server carries out, problems arise when locks duration becomes long. i.e. a few
seconds and this is when sp_locks step in.


sp_locks is a
useful tool that can help you in detecting and troubleshooting blocking and
concurrency scenarios.

Unlike sp_helpindex2
which retrieves information related to the context of the current database sp_locks
retrieves server level information and therefore does not need to be marked as
a system stored procedure. However, by using the prefix sp_ we take
advantage of the fact that sql server first searches the master database for
any object prefixed with sp_ allowing us to call the procedure from any
database without the need to use a three part name (i.e. database.schema.object)
making it’s usage more convenience.

The procedure
retunes 3 result sets at the most and this is controlled by the @Mode input

The first
result set returns information about the Lead Blocker process if exists and
is aimed towards a situation where there is a high blocking activity resulting
in a Blocking Chain.

A blocking
chain is the situation where there are many processes that are blocking other
processes that are now being blocked and in many cases requires a manual
interference to get out of the situation, typically issuing a kill command to
terminate the process that is the Lead Blocker.

The Lead
Blocker is a nickname we call the process that is the root cause, the
initiator, that’s the process that ‘started’ the blocking chain. i.e. a process
that blocks a second process that now since it is being blocked does not
release it’s granted locks thus becoming a blocker itself and this can go on and
on while other processes join the party resulting in a chain of blocks.

In a such a
scenario database concurrency is negatively effected and this of course gets
reflected in the application’s response time so at the moment you become aware
of the situation what you really want to do is handle it as soon as you can to
minimize the effect on your system.

This result
set will return a single row in most cases but under an extreme blocking
scenario it is not unlikely that you will see more than a single row returned (meaning
we have more than one Lead Blocker or more than one Blocking Chain).

In the test
script scenario’s screen shot that I captured the lead blocker is the process
issuing the UPDATE command  (spid 218). This process is blocking the first
process (spid 193) that issues the SELECT statement (/*
connection 1 */) and every additional SELECT statement issued there after
is blocked by that first SELECT statement that is blocked by the process that
issued the UPDATE command.

the connection that issued the UPDATE command will let all the blocked
(waiting) processes to proceed with their tasks and resolve the blocking
scenario and get rid of the blocking chain. So in this case the process issuing
the UPDATE command is the lead blocker.

Note that if
you terminate any other process you don’t resolve the blocking scenario and the
blocking chain remains.

The second
result set retrieves information about processes blocking and processes being
blocked by querying three key DMVs: sys.dm_os_waiting_tasks,
sys.dm_tran_locks and sys.sysprocesses.

We query sys.dm_os_waiting_tasks because that is actually
what we are intrested in – tasks (processes) that are now waiting, waiting to
be granted locks on resources in our case. The cool part is that this DMV can
be joined to the sys.dm_tran_locks on the resource_address column which is the memory
address of the resource for which the task is waiting for and on the other side
of the join condition we have the lock_owner_address column in sys.dm_tran_locks
which is the memory address of the internal data structure that is used to track
the lock request by the LOCK_MANAGER module in SQL Server.

These two
DMVs expose valuable information when it comes to locking and blocking. Next we
add sys.sysprocesses to get higher level information such as the program
and host blocking. In order to retrieve information for both processes, the
blocker process and the process being blocked we join sys.sysprocesses twice
while one instance represents the blocker and the second instance represents
the process being blocked.

This is
achieved by joining one instance of sys.sysprocesses as p1 with sys.dm_os_waiting_tasks
on the session_id column while the second instance of of sys.sysprocesses
is joined as p2 on  sys.dm_os_waiting_tasks’s blocking_session_id

The @Wait_Duration_ms
input parameter allows you to limit the rows returned to those processes who’s
wait time exceeds the number (an integer representing the time duration in milliseconds)
you provide. This is very useful because typically you have no interest in
short term duration (i.e. two or three seconds) waits (locks) that get released
and have no negative impact on the system.

The third
result set retrieves information from sys.dm_exec_requests
about active executions currently running. This result set becomes useful when
there is no blocking activity taking place and so no rows are returned by the
first two result sets but you still want to see what executions are now running
on the server.


Pages: 1 2


No comments yet... Be the first to leave a reply!