Locking and Blocking, Family Management.

Facebooktwitterredditpinterestlinkedinmail

For any parents out there of multiple children, you are acutely aware of locking and blocking in the real world.  Who gets to use the bathroom first?  Hearing the phrase “I saw that first”.  I want that.  Fighting over a toy.  Each parent has their own methods of how to resolve these disputes.

Just as in a home with siblings, locking and blocking are absolutely a part of life in SQL Server and most RDBMS. Understanding how locking and blocking occurs and then minimizing the affects in accessing our precious information is a high priority.

Database Engines treat the data as children in this respect.  Keeping the data as orderly as possible, without too much fighting.

First let’s take a look at some basic principles.

ACID

ACID is an acronym that stands for Atomicity Consistency Isolation Durability. This is a principle that Database professionals will look to when evaluating basics of a database. This basically means that the data can be trusted.

  • Atomicity – A transaction is an entire unit of work. All or NONE of the transaction is committed.
  • Consistency – All of the data is in a state of equilibrium. Indexes, materialized views and anything else affected by the transaction is consistent and in a zen state.
  • Isolation – The idea that the data modifications are left alone until they are finished. Transactions are kept away from each other. Like two children being sent to their rooms to clean.
  • Durability – The RDBMS keeps track of these changes and that after the transaction is complete, that data is recorded in the database permanently and can be recovered in the event of a system failure.

 

Isolation Levels

In SQL Server, and other RDBMS, the engine implements the configured isolation level which will affect the ACIDity when reading data. The isolation level will isolate the process and protect it from other transactions. To do this, locking is required. The locking is determined by the isolation level. Low levels of isolation will give high levels of concurrency and shared use of the data, but can result in lower levels of consistency with dirty or phantom reads.

SQL Server will use isolation levels to command the read behavior of the database/transaction.

  • Read Uncommited
    • Read data from other transactions yet completed
    • Dirty Reads
    • Uncommitted updates
  • Read Committed
    • SQL Server Default
    • Prevents reading Data from uncommitted transactions
    • Can result in Phantom Reads/Repeatable Reads
  • Repeatable Read
    • This will ensure that if data is reread within a transaction, that the data does not change
    • No transaction can modify the data until the transaction reading the data is complete
    • This comes at the cost that all shared locks in the read will hold until the transaction is completed
    • This will eliminate Dirty and Non-Repeatable reads, but Phantom reads may still occur.
  • Serializable
    • This is putting the transactions in a single line.
    • This is the most consistent and best way to ensure for ACIDity.
    • A read/write lock will be issued for all of the records affected by the transaction
    • This includes Repeatable Read isolation and will also eliminate Phantom reads.
  • Snapshot
    • This will read the data as it is at the beginning of the transaction.
    • This will come at a high cost to tempdb as the data for the snapshot is stored in tempdb.
    • This will eliminate Dirty, Phantom, Non-Repeatable Read and Lost updates

Lower isolation levels offer greater concurrency, meaning more connections may view the data, but the following concurrency affects may occur:

  • Lost Updates – Transaction A changes a value, then commits. Transaction B then also changes that value and commits. Transaction A then reads the data after Transaction B commits. Transaction A is expecting the first value, but obtains the second
  • Dirty Reads – Reading data when another process is changing the data and the original process is reading uncommitted data.
  • Non-repeatable Reads – This occurs when a process reads the same data multiple times within a session. Between reads of the data, another process may change the data and therefore different values are returned.
  • Phantom Reads – This is similar to Non-Repeatable reads, but instead of changing(updating) the data, another process adds or removes records between the reads of the same data during a process.

Locking Explained

Locking is a mechanism used by the SQL Server Database Engine to synchronize access by multiple users to the same piece of data at the same time.  Locks are managed internally by a part of the Database Engine called the lock manager.

Each transaction will lock levels of resources such as rows, pages or tables for which the transaction is dependent.  The transaction will release the lock when it either commits or rolls back.  The amount of time the lock is held is dependent on the isolation level.

The two basic types of locks are read locks and write locks.

  • Read Locks
    • Shared –While a shared lock is being held other transactions can read but cannot modify locked data.  The lock is released after the locked data has been read unless the isolation level is at or higher than Repeatable Read or a locking hint such as READCOMMITED or READCOMMITTEDLOCK is used.
    • Concurrent – This is when you read data using read data held by exclusive lock only by specifying a NOLOCK hint or using a read uncommitted isolation level.
  • Write Locks
    • Update – Update locks are a mix of shared and exclusive locks. When an update statement is executed, SQL Server has to find the data it wants to modify first, so to avoid lock conversion deadlocks an update lock is used. Only one update lock can be held on the data at one time, similar to an exclusive lock. The update lock itself can’t modify the underlying data, when the data is modified, it is transferred to an exclusive lock.
    • Exclusive – Exclusive locks are used to lock data being modified by one transaction thus preventing modifications by other concurrent transactions.  A transaction always gets an exclusive lock on any data it modifies, and holds that lock until the transaction completes, regardless of the isolation level set for that transaction.

 

Lock Granularity/Escalation

Lock Escalations are an optimization technique used by SQL Server to control the amount of locks that are held within the Lock Manager of SQL Server.

Lock granularity consists of DATABASE, TABLE, PAGE and ROW locks.  As soon as you connect to a SQL Server database, a shared lock is issued on the database level itself. This prevents another user from performing an action such as “DROP DATABASE” while there is a connection to it.

SQL Server will perform it’s lock granularity from top down. First, it will check if a table lock is necessary, then page then row. An Intent(Shared or Exclusive), dependent if the operation is read or write, is issued on the table and page and a shared lock on the row. If the amount of data necessary is more than a row and on a page. Once SQL Server needs more than 5,000 row locks, it will escalate(by default) to a table lock.

You can alter these defaults by either using query hints such as ROWLOCK, PAGLOCK or TABLOCK. You can also alter the lock escalation of each table by using the following:

ALTER	TABLE MyTable
SET	LOCK_ESCALATION = <AUTO, TABLE, DISABLE>

This is generally not recommended as it is best to allow the Database Engine to escalate locks accordingly. Lower level locks increase concurrency, but consume more memory.

 

Fun Scripts

So, we now know that locking and blocking are part of the natural occurrence of processing transactions.  When it comes to blocking the objective is to lessen the affects and hold the blocks as little time as possible.  Below, you will find two scripts.  The first will list was locking is occurring within your database.  The second will list your current processes and list the ones blocking first.  These may help in ascertaining what issues need to be addressed regarding blocking.

Current processes, showing blocking first

if	object_id('tempdb..#tmpdm_exec_sessions') is not null
	drop	table #tmpdm_exec_sessions

if	object_id('tempdb..#tmpdm_exec_requests') is not null
	drop	table #tmpdm_exec_requests

if	object_id('tempdb..#tmpdm_exec_connections') is not null
	drop	table #tmpdm_exec_connections

if	object_id('tempdb..#tmpsysprocesses') is not null
	drop	table #tmpsysprocesses

if	object_id('tempdb..#tmpdm_os_waiting_tasks') is not null
	drop	table #tmpdm_os_waiting_tasks

select	*
into	#tmpdm_exec_sessions
from	sys.dm_exec_sessions

select	*
into	#tmpdm_exec_requests
from	sys.dm_exec_requests

select	*
into	#tmpdm_exec_connections
from	sys.dm_exec_connections

select	*
into	#tmpsysprocesses
from	sys.sysprocesses

select	*
into	#tmpdm_os_waiting_tasks
from	sys.dm_os_waiting_tasks

select	distinct *
from	(
	SELECT	s.session_id AS spid			,
		s.[status]				,
		s.login_name AS loginName		,
		s.[host_name] AS hostName		,
		DB_NAME(s2.dbid) AS dbName		,
		r.command				,
		s.cpu_time AS cpuTime			,
		s.reads + s.writes AS diskIO		,
		s.last_request_end_time AS lastBatch	,
		s.[program_name] AS programName		,
		s.session_id				,
		r.request_id				,
		CASE	s.transaction_isolation_level
			WHEN 0 THEN 'Unspecified' 
			WHEN 1 THEN 'ReadUncommitted' 
			WHEN 2 THEN 'ReadCommitted' 
			WHEN 3 THEN 'Repeatable' 
			WHEN 4 THEN 'Serializable' 
			WHEN 5 THEN 'Snapshot' 
		END	AS transactionIsolationLevel	,
		OBJECT_NAME(t.objectid) AS objectName	,
		SUBSTRING	(t.text,r.statement_start_offset/2,
			(
			CASE
				WHEN r.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX),t.text)) * 2
				ELSE r.statement_end_offset
			END - r.statement_start_offset
			)/2
			) as IndividualQuery		,
		t.[text] AS ParentQuery			,
		w.blocking_session_id			,
		r.percent_complete			,
		r.estimated_completion_time		,
		r.status as request_status		,
		r.wait_type
	FROM	#tmpdm_exec_sessions AS s 
		LEFT JOIN #tmpdm_exec_requests AS r ON r.session_id = s.session_id 
		LEFT JOIN #tmpdm_exec_connections AS c ON c.session_id = s.session_id 
		CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) AS t 
		inner join #tmpsysprocesses s2 on s2.spid = s.session_id
		LEFT join #tmpdm_os_waiting_tasks w on w.session_id = s.session_id
	WHERE	s.is_user_process = 1
	) a
where	a.[status] != 'sleeping'
order	by a.blocking_session_id desc

 

 Locks currently held within the database

set	nocount on
/*****************************************************************************************************
**
*****************************************************************************************************/
	declare	@dm_tran_locks table
		(
		SPID				int		,
		DBName				varchar(256)	,
		LockedResource			nvarchar(60)	,
		resource_associated_entity_id	bigint		,
		resource_database_id		int		,
		LockType			nvarchar(60)
		)

	declare	@sys_partitions table
		(
		hobt_id				bigint		,
		[object_id]			int
		)

	declare	@sys_objects table
		(
		object_id			int		,
		name				varchar(256)
		)

	declare	@sys_dm_exec_sessions table
		(
		session_id			smallint	,
		login_name			nvarchar(128)	,
		host_name			nvarchar(128)
		)

	declare	@sys_dm_exec_connections table
		(
		auth_scheme			nvarchar(40)	,
		session_id			smallint	,
		most_recent_sql_handle		varbinary(64)
		)
/*****************************************************************************************************
**
*****************************************************************************************************/
	insert	into @dm_tran_locks
		(
		SPID				,
		DBName				,
		LockedResource			,
		resource_associated_entity_id	,
		resource_database_id		,
		LockType
		)
	select	l.request_session_id		,
		db_name(l.resource_database_id)	,
		l.resource_type			,
		l.resource_associated_entity_id	,
		l.resource_database_id		,
		l.request_mode
	from	sys.dm_tran_locks l
	WHERE   l.resource_database_id = db_id()

	insert	into @sys_partitions
		(
		hobt_id				,
		[object_id]
		)
	select	p.hobt_id			,
		p.object_id
	from	sys.partitions p

	insert	into @sys_objects
		(
		object_id			,
		name
		)
	select	object_id, name
	from	sys.objects s

	insert	into @sys_dm_exec_sessions
		(
		session_id			,
		login_name			,
		host_name
		)
	select	c.session_id, c.login_name, c.host_name
	from	sys.dm_exec_sessions c

	insert	into @sys_dm_exec_connections
		(
		auth_scheme			,
		session_id			,
		most_recent_sql_handle
		)
	select	c.auth_scheme, c.session_id, c.most_recent_sql_handle
	from	sys.dm_exec_connections c
/*****************************************************************************************************
**
*****************************************************************************************************/

	SELECT  l.SPID					,
		l.DBName				,
		o.Name		AS LockedObjectName	,
		p.object_id	AS LockedObjectId	,
		l.LockedResource			,
		l.LockType				,
		ST.text		AS SqlStatementText	,        
		ES.login_name	AS LoginName		,
		ES.host_name	AS HostName		,
		CN.auth_scheme	as AuthenticationMethod
	FROM    @dm_tran_locks l
		inner join @sys_partitions p on p.hobt_id = l.resource_associated_entity_id
		inner join @sys_objects o on o.object_id = p.object_id
		inner join @sys_dm_exec_sessions es on es.session_id = l.SPID
		inner join @sys_dm_exec_connections cn on cn.session_id = es.session_id
		CROSS APPLY sys.dm_exec_sql_text(cn.most_recent_sql_handle) st
/*****************************************************************************************************
**
*****************************************************************************************************/
	SELECT  l.SPID					,
		o.Name		AS LockedObjectName	,
		l.LockedResource			,
		l.LockType				,
		count(1)	as lock_count		
	FROM    @dm_tran_locks l
		inner join @sys_partitions p on p.hobt_id = l.resource_associated_entity_id
		inner join @sys_objects o on o.object_id = p.object_id
	group	by l.SPID, o.name, l.LockedResource, l.LockType

 

Coming up: Deadlocks – Who gets the toy?

References:

http://www.sqlteam.com/article/introduction-to-locking-in-sql-server#sthash.ujDqLUPK.dpuf

http://blogs.msdn.com/b/sqlcat/archive/2011/02/20/concurrency-series-basics-of-transaction-isolation-levels.aspx

https://technet.microsoft.com/en-us/library/jj856598(v=sql.110).aspx

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *