We're using a SQL Server 2005 database (no row versioning) with a huge select statement, and we're seeing it block other statements from running (seen using sp_who2
). I didn't realise SELECT statements could cause blocking - is there anything I can do to mitigate this?

- 41,080
- 29
- 148
- 220
-
Do you use higher isolation levels by any chance, like repeatable read or serializable? Some ADO and CLR components sneak in searializable without your explicit consent... What is the blocking resource on (key, range, table) ? – Remus Rusanu Jun 19 '09 at 10:09
-
I'm not specifying the isolation level. Is there a way of finding out what isolation level is in use? – Neil Barnwell Jun 19 '09 at 10:15
-
2Yes, check out sys.dm_exec_sessions. transaction_isolation_level column. – Remus Rusanu Jun 19 '09 at 10:27
-
It would helpful to have some description of the tables and data involved. If you can optimize the query then the locks will obviously be held for a shorter duration. Another option if it is a read-only transaction is to retrieve the set of data you need to operate on to temp tables, and then query against the temp tables. You can populate all of your temp tables under a high isolation level to guarantee a transactional "snapshot" of the live tables. – ahains Jun 19 '09 at 13:29
6 Answers
SELECT can block updates. A properly designed data model and query will only cause minimal blocking and not be an issue. The 'usual' WITH NOLOCK hint is almost always the wrong answer. The proper answer is to tune your query so it does not scan huge tables.
If the query is untunable then you should first consider SNAPSHOT ISOLATION level, second you should consider using DATABASE SNAPSHOTS and last option should be DIRTY READS (and is better to change the isolation level rather than using the NOLOCK HINT). Note that dirty reads, as the name clearly states, will return inconsistent data (eg. your total sheet may be unbalanced).

- 288,378
- 40
- 442
- 569
-
1Quoted that on another question: http://stackoverflow.com/questions/1018651/nolock-vs-transaction-isolation-level/1018676#1018676 – instanceof me Jun 19 '09 at 15:47
-
3Don't get me wrong, an excellent answer, but I try not to speak in absolutes without knowing anything about their application and design. For example, what if your app was an Enterprise app, that had a 'Query' feature? Guess who is writing your query? To your database? The customer! There's not enough indexes out there!! Snapshot isolation levels? How many people are simultaneously hitting your databases? Because let's talk about resources with this option!! So, yes, us unwashed masses that aren't DB gurus have to use a dirty read! – JustLooking May 20 '13 at 17:31
-
@JustLooking you have good points, but I think Remus' answer is a good guideline - esp. his first argument that requiring nolock hints there is something wrong with the overall design or query. Also what about a db that uses transactions? Shouldn't that indicate nolock should be avoided? It seems like it would create many obscure and difficult to test bugs because they are intermittent. – Morgan T. Jul 10 '13 at 20:21
-
2"The 'usual' WITH NOLOCK hint is almost always the wrong answer. " What if my question is: I want to do dirty, non blocking SELECT statements that are currently causing 20-60 seconds of blocking. Then what is the right answer? – Jonesome Reinstate Monica Mar 21 '18 at 22:04
From documentation:
Shared (S)
locks allow concurrent transactions to read(SELECT)
a resource under pessimistic concurrency control. For more information, seeTypes of Concurrency Control
. No other transactions can modify the data whileshared (S)
locks exist on the resource.Shared (S)
locks on a resource are released as soon as the read operation completes, unless the transaction isolation level is set to repeatable read or higher, or a locking hint is used to retain theshared (S)
locks for the duration of the transaction.
A shared lock
is compatible with another shared lock or an update lock, but not with an exlusive lock.
That means that your SELECT
queries will block UPDATE
and INSERT
queries and vice versa.
A SELECT
query will place a temporary shared lock when it reads a block of values from the table, and remove it when it done reading.
For the time the lock exists, you will not be able to do anything with the data in the locked area.
Two SELECT
queries will never block each other (unless they are SELECT FOR UPDATE
)
You can enable SNAPSHOT
isolation level on your database and use it, but note that it will not prevent UPDATE
queries from being locked by SELECT
queries (which seems to be your case).
It, though, will prevent SELECT
queries from being locked by UPDATE
.
Also note that SQL Server
, unlike Oracle
, uses lock manager and keeps it locks in an in-memory linked list.
That means that under heavy load, the mere fact of placing and removing a lock may be slow, since the linked list should itself be locked by the transaction thread.

- 413,100
- 91
- 616
- 614
-
1Don't assume implementation details about SQL Servere's lock manager. Suffice to say is *not* a linked list. – Remus Rusanu Jun 19 '09 at 11:38
-
Is there any way to get around the shared lock in MySQL? I attempted to use every Isolation level available, but none of them prevent update queries from being locked by select (which is my case). – Travis J Nov 18 '14 at 00:01
-
@TravisJ: please post it as a separate question, providing a reproducible use case. – Quassnoi Nov 18 '14 at 07:28
To perform dirty reads you can either:
using (new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
//Your code here
}
or
SelectCommand = "SELECT * FROM Table1 WITH (NOLOCK) INNER JOIN Table2 WITH (NOLOCK) ..."
remember that you have to write WITH (NOLOCK) after every table you want to dirty read

- 11,520
- 7
- 36
- 50
You could set the transaction level to Read Uncommitted

- 29,228
- 19
- 111
- 160
-
Isn't that the same as using (NOLOCK)? I don't really want to go that direction... – Neil Barnwell Jun 19 '09 at 10:08
-
-
2No, is not fine for read only queries, since NOLOCK can *only* be applied to read-only queries, so any problem it has (and has plenty) applies to 'read-only' too. An unbalanced sheet report is still 'read-only', but hardly correct. – Remus Rusanu Jun 19 '09 at 11:01
You might also get deadlocks:
"deadlocks involving only one table" http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/01/reproducing-deadlocks-involving-only-one-table.aspx
and or incorrect results:
"Selects under READ COMMITTED and REPEATABLE READ may return incorrect results."

- 16,804
- 8
- 54
- 74
You can use WITH(READPAST)
table hint. It's different than the WITH(NOLOCK)
. It will get the data before the transaction was started and will not block anyone. Imagine that, you ran the statement before the transaction was started.
SELECT * FROM table1 WITH (READPAST)

- 3,161
- 6
- 25
- 40
-
READPAST is a good option. But keep in mind it skips records that are blocked. You may get fewer records. – Guy Biber Apr 28 '22 at 13:55