SQL Server Performance Tuning

 

Tips On How to Minimize SQL Server Blocking

 

Blocking occurs when one connection to SQL Server locks one or more records, and a second connection to SQL Server requires a conflicting lock type on the record or records locked by the first connection. This causes the second connection to wait until the first connection releases its locks. By default, a connection will wait an unlimited amount of time for the blocking lock to go away. Blocking is not the same thing as a deadlock.

A certain amount of blocking is normal and unavoidable. But too much blocking can cause connections (representing applications and user) to wait extensive periods of time, hurting overall SQL Server performance.  In the worst cases, blocking can escalate as more and more connections are waiting for locks to be released, creating extreme slowdowns. The goal should be to reduce blocking as much as possible. Locks held by SELECT statements are only held as long as it takes to read the data, not the entire length of the transaction. On the other hand, locks held by INSERT, UPDATE, and DELETE statements are held until the entire transaction is complete. This is done in order to allow easy rollback of a transaction, if necessary.

Some causes of excessive blocking, and ways to help avoid blocking, include:

  • Long-running queries. Anytime a query of any type, whether it is a SELECT, INSERT, UPDATE, or DELETE, takes more than a few seconds to complete, blocking is likely. The obvious solution to this is to keep transactions as short as possible. There are many tips on this website on how to help reduce transaction time, but some of them include: optimize Transact-SQL code; optimize indexes; break long transactions into multiple, smaller transactions; avoiding cursors, etc.
  • Canceling queries, but not rolling them back. If your application's code allows a running query to be cancelled, it is important that the code also roll back the transaction. If this doesn't happen, locks held by the query will not be released, which means blocking can occur.
  • Distributed client/server deadlock. No, this is not your typical deadlock that is handled automatically by SQL Server, but a very special situation that is not automatically resolved by SQL Server.

    Here's what can happen. Let's say that an application opens two connections to SQL Server. The application then asynchronously starts a transaction and sends a query through the first connection to SQL Server, waiting for results. The application then starts a second transaction and sends a query through the second connection to SQL Server, waiting for results. At some point, one of the queries from one of the connections will begin to return results, and the application will then begin to process them.

    As the application processes the results, at some point what could happen is that the remainder of the results become blocked by the query running from the other connection. In other words, the first query can't complete because it is being blocked by the second query. So in essence, this connection is blocked and cannot continue until the second query completes. But what happens is that the second query tries to return its results, but because the application is blocked (from the first query), its results cannot be processed. So this means that this query cannot complete, which means the block on the first query can never end, and a deadlock situation occurs. Neither connection will give up, so neither connection never ends, and the deadlock situation never ends.

    SQL Server is unable to resolve this type of deadlock, so unless you want to write applications that hang forever, you can take these steps to prevent this unusual situation: 1) Add a query time-out for each of the queries, or 2) Add a lock time-out for each of the queries, or 3) Use a bound connection for the application.

In many ways, the best way to avoid blocking is to write well-tuned applications that follow the tuning advice found on this website. [7.0, 2000] Updated 1-18-2004

*****

By default, blocking locks don't time out. The waiting connection waits until the lock is released, and the block is over. If you like, you can set a lock time-out so that a connection does not wait indefinitely for the blocking lock to be released. This is accomplished using the LOCK_TIMEOUT setting.

When the LOCK_TIMEOUT setting is used to set a maximum amount of time that a connection can wait for a blocking lock to go away. This means that the connection that has the lock and is causing the blocking problem is not affected, but that the connection waiting for the block is halted, and receives an error message. When this happens, then error message 1222, "Lock request time-out period exceeded" is sent to the application.

This means that the application needs to include the appropriate error-handling code to deal with this situation and take the appropriate action, which includes rolling back the transaction. If the application does not know how to deal with this error message, and the transaction is not rolled back, it is possible that the application can continue as if the transaction was not automatically cancelled. Because of this, you should not use the LOCK-TIMEOUT setting unless your application(s) that will be affected by it know what to do when they receive this message from SQL Server.

The syntax for the SET LOCK_TIMEOUT is:

SET LOCK_TIMEOUT timeout_period

where timeout_period is the number of milliseconds that a connection waits for a blocking lock to go away before an error is returned from SQL Server to the application. A value of -1 is the default, which means to wait indefinitely. A value of 0 tells SQL Server not to wait at all, and to return the error immediately.

This command is based on a per connection basis, and stays with the connection until the connection is broken, or a new SET LOCK_TIMEOUT command is issued. Updated [7.0, 2000] 1-18-2004

*****

Avoid INSERTing, UPDATEing, or DELETEing large numbers of records in a single transaction. If you do, all the records affected by your action will be locked until the transaction is done. If you find that you need to perform mass data changes, it is better to batch them into smaller, shorter transactions to prevent unnecessary locking.

In addition, changing the backup method from full to simple will also reduce the overhead incurred by long running transactions. Once you are done with the long running activity, you can switch back to the full backup method. [6.5, 7.0, 2000] Updated 1-18-2004

*****

One way to help identify blocking locks is to use Enterprise Manager. If you expand "Process Info" Under "Current Activity," for the appropriate server, and then scroll to the right of the screen, you will see if there are currently any blocking locks. If you do, you will see which SPID is blocking what other SPIDs. Unfortunately, this screen is not dynamically updated, so if you will want to refresh this screen often if you are looking for blocking locks. To refresh the screen, right-click on "Current Activity," not "Process Info," and then select "Refresh."

Most blocking locks go away soon. But if a blocking lock does not go away, and it is prevent one or more users from performing necessary tasks, you can ask the user, whose SPID is causing the blocking, to exit their program that is causing the block. Or, you can KILL the blocking SPID from Enterprise Manager. KILLing the blocking SPID will cause the current transaction to rollback and allow the blocked SPIDs to continue. [7.0, 2000] Updated 1-18-2004

*****

To help identify and correct queries that cause blocking locks, you need to find out what the query looks like that is causing the blocking lock. Before you can do this, you must first identity the SPID that is causing the blocking. Once you have done that, there are two ways to view the query that is causing the blocking lock.

First, from Enterprise Manager, in the "Process Info" window located under "Current Activity," right-click on the SPID causing the blocking and choose "Properties." This will display the query.

Second, from Query Analyzer, enter this code to reveal the query causing the blocking lock:

DBCC INPUTBUFFER (<spid>)

Once you have identified the query that is causing the blocking lock, you can begin researching it to see if there is anything you can do to modify the query to avoid blocking in the future. [7.0, 2000] Updated 1-31-2004

*****

To help identify the type of lock that a blocking lock is holding, you must first identify the SPID that is causing the blocking, Once you have that, there are two ways to view the type of lock being held by the blocking lock:

First, from Enterprise Manager, in the "Locks/Process ID" window located under "Current Activity," locate the SPID causing the blocking lock, and click on it. The type of lock will be displayed in the right-hand window.

Second, run the command:

sp_lock

in Query Analyzer. You will then have to match the SPID of the block lock to the SPID listed in the results of this command.

Knowing the type of lock held by the blocking lock can help you figure out why the query in question is causing a blocking lock. [7.0, 2000] Updated 1-31-2004

*****

One way to help identify blocking locks is to use the SQL Server Profiler. The Profiler is useful for capturing blocking locks because it can capture blocking locks over time, unlike the Enterprise Manager, which only shows blocking locks as of the current instant. In addition, the query that is available from the INPUTBUFFER may not be enough information to diagnose a blocking problem. Sometimes, queries that run just before the query that is causing the blocking is related to the blocking problem. By performing a Profiler Trace, you can see all the queries and other activity that precede a blocking lock. In order to use the trace data, you will have to know the SPID that caused the blocking lock, and then look up the data from the trace for this one particular SPID.

Below are two different Profiler configurations, one for SQL Server 7.0 and one for SQL Server 2000.

SQL Server 7.0 Profiler Configuration

Events

Error and Warnings: Exception
Misc: Attention
Misc: Execution Plan
Sessions: Connect
Sessions: Disconnect
Sessions: Exiting Connection
TSQL: RPC:Starting
TSQL: RPC:Completed
TSQL: SQL:BatchStarting
TSQL: SQL:BatchCompleted
Stored Procedures: SP:StmtStarting
Stored Procedures: SP:StmtCompleted
Transactions: SQLTransaction

Data Columns

Group By: SPID
Event Class
Text
Integer Data
Binary Data
Application Name
NT User Name
SQL User Name
Start Time
End Time
Connection ID

Filters

Trace Event Criteria: Severity (type 24 in the Maximum box)
Add other filters as desired to reduce a flood of too much data

 

SQL Server 2000 Profiler Configuration

Events

Error and Warnings: Exception
Error and Warnings: Attention
Performance: Execution Plan
Sessions: Exiting Connection
Stored Procedures: RPC: Starting
Stored Procedures: RPC: Completed
Stored Procedures: SP: Starting
Stored Procedures: SP: Completed
Stored Procedures: SP: StmtStarting
Stored Procedures: SP: StmtCompleted
Transactions: SQLTransaction
TSQL: SQL:BatchStarting
TSQL: SQL:BatchCompleted

Data Columns

Group: SPID
EventClass
TextData
IntegerData
BinaryData
ApplicationName
NTUserName
LoginName
StartTime
EndTime

Filters

Trace Event Criteria: Severity (type 24 in "Less than or equal" box)
Add other filters as desired to reduce a flood of too much data

One of the best ways to use the Profiler is to begin a trace, duplicate the activity that causes the blocking lock, identify the SPID of the blocking lock in Enterprise Manger, and then stop the trace. Next, look up the SPID in the trace, viewing all of the activity that occurred up to the blocking lock occurring. [7.0, 2000] Updated 1-31-2004

*****

To help identify blocking in your databases, Microsoft has two separate stored procedures listed on their website (one each for SQL Server 7.0 and 2000) you can use to help identify blocking problems on your SQL Servers. On these pages are scripts you can use to create stored procedures that you can run anytime to help you identify blocking issues.

INF: How to Monitor SQL Server 7.0 Blocking (Q251004)

INF: How to Monitor SQL Server 2000 Blocking (Q271509)

[7.0, 2000] Added 8-9-2004

*****

One way to help avoid blocking locks is to ensure that your tables have appropriate indexes. This is because the quicker that SQL Server can find the data it is looking for, the less time locks have to be in place, reducing the potential for blocking locks to occur in the first place.

For example, if SQL Server needs to perform an UPDATE to a row, and there is not an appropriate index to quickly find the row that needs to be UPDATED, then a table scan must be performed. While SQL Server is examining every row in the table (which can take a long time if there are many rows), SQL Server has to maintain an UPDATE lock on the entire table during the duration of the table scan, potentially causing much blocking.

Table scans are not the only performance problem. Poorly designed indexes, while perhaps faster than a table scan, can be much slower than a well-designed index, potentially contributing to blocking. It is always in your best interest to optimize indexes so they perform at their maximum. [6.5, 7.0, 2000] Added 8-9-2004

 

 

1