In SQL Server, few hints are as popular and controversial as NOLOCK.
Many developers, and even some DBAs, quickly use NOLOCK when they face a slow or blocked query:
SELECT *
FROM Orders WITH (NOLOCK);
Suddenly, the query runs.
The problem seems to be solved.
But what really happened?
Did SQL Server become faster?
Was the blocking really removed?
Or did we simply decide not to wait, even if this means reading incorrect data?
In many cases, the last answer is closer to the truth.
What Does NOLOCK Actually Do?
In SQL Server, NOLOCK is equivalent to using the READ UNCOMMITTED isolation level for that table.
Normally, with READ COMMITTED, a query cannot simply read data that is being changed by another transaction. Because of this, the query may have to wait for locks held by the transaction that is writing the data.
However, READ UNCOMMITTED allows a query to read data that has not been committed by another transaction yet.
As a result, many types of blocking caused by data locks between readers and writers can be reduced.
This is exactly why NOLOCK is so popular.
But there is an important point:
NOLOCK does not mean "no locks".
Even queries that use NOLOCK still need a Schema Stability Lock, or Sch-S, to access metadata.
Therefore, NOLOCK does not protect you from every type of blocking. For example, a DDL operation that takes a Sch-M lock can still block your query.
So, the first common misunderstanding is:
NOLOCK means my query will never be blocked again.
No. It does not.
The Main Problem: Dirty Read
The biggest cost of using NOLOCK is giving up some of the guarantees provided by READ COMMITTED.
Consider the following transaction:
BEGIN TRANSACTION;
UPDATE Accounts
SET Balance = Balance - 1000000
WHERE AccountId = 10;
-- COMMIT has not happened yet
At the same time, another query runs with NOLOCK:
SELECT Balance
FROM Accounts WITH (NOLOCK)
WHERE AccountId = 10;
The query may read the new value even though the first transaction has not been committed yet.
If the first transaction later executes:
ROLLBACK;
the value read by the second query was never a final change in the database.
This is called a Dirty Read.
In other words, you have read something that may not exist a few moments later.
The Problem with NOLOCK Is Not Only Dirty Read
If we think that the only problem with NOLOCK is reading uncommitted data, we are missing an important part of the story.
In some situations, while data is being scanned, data pages and rows can be moved or changed. Queries using NOLOCK may then return unreliable results.
For example:
-
A row may be read more than once.
-
A row may be missed completely.
-
You may read data that is later rolled back.
-
The query result may not match the logical state you expect from the data.
This is especially important for queries that scan a large amount of data.
In some situations, SQL Server can even return Error 601:
Could not continue scan with NOLOCK due to data movement.
This shows that using NOLOCK is not simply about accepting "a small risk in data accuracy." In some situations, the read operation itself can have a problem.
A Simple Question: Can Your Financial Report Be Dirty?
Imagine that you have a management report that calculates total sales for the month:
SELECT SUM(Amount)
FROM Sales WITH (NOLOCK)
WHERE SaleDate >= '2026-08-01';
If several transactions are inserting or updating data at the same time, do you really want your report to include data that has not been committed yet?
Now imagine that this report is used for decisions such as:
-
Bonus payments
-
Profit calculation
-
Inventory management
-
Customer balance calculation
-
Financial reporting
-
Management decisions
In such cases, is making the query a few milliseconds faster really worth reducing the reliability of the data?
Probably not.
So Why Is NOLOCK So Popular?
Because the real problem is often somewhere else.
Imagine that we have a long-running transaction:
BEGIN TRANSACTION;
UPDATE Orders
SET Status = 'Processed'
WHERE OrderId = 1001;
-- Long operation
EXEC SomeLongRunningProcedure;
-- Other operations
-- ...
COMMIT;
During this time, other queries may have to wait when they try to read the same data.
The developer sees:
"My query is blocked!"
Instead of asking:
"Why is this transaction taking so long?"
the developer says:
"Add NOLOCK and fix it."
But this does not fix the root cause.
It only changes the problem.
Find the Cause of Blocking Before Using NOLOCK
When a query is blocked, the first question should not be:
"Where should we add NOLOCK?"
The better question is:
What is causing the blocking?
The cause may be one of the following:
-
The transaction is too long.
-
The transaction starts earlier than necessary.
-
COMMIT or ROLLBACK happens too late.
-
The query needs a proper index.
-
There is a problem with the execution plan.
-
Too much data is being read or changed without a real need.
-
A batch operation is not designed properly.
-
Lock escalation has occurred.
-
The workload design does not match the data access pattern.
A professional DBA should first investigate the blocking chain and the transactions involved. Then, based on the real cause, the DBA can choose the right solution.
RCSI: A Way to Reduce Reader-Writer Blocking Without Dirty Read
This is where Read Committed Snapshot Isolation, or RCSI, becomes important.
RCSI is an important SQL Server feature that can reduce blocking between readers and writers.
You can enable RCSI at the database level:
ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON;
This changes the behavior of READ COMMITTED.
Instead of making a reader wait for the writer's lock in many situations, SQL Server can use Row Versioning to read the appropriate committed version of the data.
As a result:
Readers and writers can block each other less, without using Dirty Read to reduce blocking.
NOLOCK vs. RCSI
We can compare these two approaches in a simple way.
NOLOCK
It says:
"If the data is being changed, I will not wait, even if the change has not been committed yet."
RCSI
It says:
"I want committed data, but I do not have to wait for the writer transaction to finish before reading it."
In simple terms:
NOLOCK gets speed by reducing read consistency guarantees.
On the other hand:
RCSI tries to reduce blocking by using Row Versioning without forcing the reader to perform a Dirty Read.
RCSI Is Not Free
However, we should not think of RCSI as a "magic bullet."
Row Versioning uses system resources.
In the normal SQL Server architecture, the Version Store is located in tempdb. Keeping and cleaning up row versions can affect:
-
tempdb space
-
I/O
-
Resource usage
-
Write-heavy workloads
Also, when Accelerated Database Recovery (ADR) is enabled, the Version Store architecture can be different, and part of the versioning can be stored in the Persistent Version Store.
Therefore, before enabling RCSI on a large production database, we should review the workload, tempdb, transactions, and data access patterns.
However, this cost does not mean that RCSI is a bad choice.
For many OLTP systems, the controlled cost of Row Versioning can be much more reasonable than sacrificing data accuracy and reliability just to avoid blocking.
One Important Point: RCSI Does Not Remove All Blocking
We should not forget this.
RCSI can be very useful for reducing Reader-Writer Blocking caused by data locks, but it does not remove every type of blocking in SQL Server.
For example, schema-related locks can still cause waits.
So, if you still see blocking after enabling RCSI, it does not necessarily mean that RCSI has failed.
You may simply be dealing with a different type of blocking.
RCSI Is Not the Same as SNAPSHOT
Another common mistake is treating READ_COMMITTED_SNAPSHOT and SNAPSHOT ISOLATION as the same thing.
Both use Row Versioning, but their consistency models are different.
With RCSI, data consistency is provided at the Statement level.
This means that each statement gets a consistent view of the data based on the point in time when that statement starts.
With SNAPSHOT ISOLATION, consistency is provided at the Transaction level.
This difference is very important when designing complex systems. We should not treat these two features as the same thing just because both use Row Versioning.
Should We Remove NOLOCK?
Not necessarily.
The goal of this article is not to say:
"NOLOCK is always forbidden in every situation."
The important thing is to understand its cost and risk.
If a query really needs data that may be Dirty or Inconsistent, and this is acceptable for that specific use case, READ UNCOMMITTED may be considered.
But making this hint a general standard for all queries, such as:
SELECT ...
FROM ...
WITH (NOLOCK);
is not a good architectural decision.
This is especially true when the query is used for:
-
Financial operations
-
Accounting
-
Inventory
-
Payments
-
Orders
-
Official reports
-
Critical business decisions
If Your Query Is Blocked, NOLOCK Is Not the First Question
This may be the most important point in this article.
When a query is blocked, before adding NOLOCK, ask:
Which transaction is blocking me?
Then investigate:
-
Why is the transaction still open?
-
How long has it been open?
-
What locks has it taken?
-
What is the writer query doing?
-
Is there a proper index?
-
What does the execution plan show?
-
Can the transaction be made shorter?
-
Should the workload be designed differently?
-
Is RCSI suitable for this database?
Sometimes the best solution for blocking is not a hint.
Sometimes it is an index.
Sometimes it is fixing a long-running transaction.
Sometimes it is improving the execution plan.
Sometimes it is changing the design of a batch process.
And sometimes, RCSI is the right choice.
The important thing is to understand the real problem before choosing the solution.
Seyed Hamed Vahedi
Wed, 12 August, 2026