← Articles
Concurrency

Why NOLOCK Does Not Fix Blocking Problems

NOLOCK trades normal read consistency for the possibility of incorrect results, yet it does not remove every kind of blocking. Reliable fixes begin with the blocking chain, transaction scope, access paths, and isolation requirements.

Adding NOLOCK is a common response when a SQL Server query waits behind another transaction. It can make a read stop waiting for ordinary data locks because it requests the read-uncommitted isolation level. That changes the correctness contract of the query. It does not diagnose the blocker, shorten the transaction, improve the access path, or guarantee that the statement will never wait.

Read uncommitted means the result may be wrong

A dirty read can return a value written by a transaction that later rolls back. The problem is broader than seeing an uncommitted balance. While pages and rows change, an allocation-order scan may miss rows, encounter rows more than once, or combine values that never existed together as a committed state. Repeating the query can produce a different answer even when the application believes it is reading one logical moment.

Those risks matter for invoices, inventory, eligibility, reconciliation, operational decisions, and reports that are expected to tie out. A disclaimer that the report is approximate does not necessarily make internal inconsistency acceptable. The business owner of the data should decide the required consistency, not inherit it from a query hint added during an incident.

NOLOCK does not mean no locks

The hint affects data-lock behavior, but SQL Server still uses locks for metadata and schema coordination. A query can wait for a schema modification lock during a deployment or index operation. It can also wait on latches, memory, CPU scheduling, storage, network activity, or a worker thread. None of those waits is repaired by read-uncommitted access.

The hint also does nothing to make a writing transaction shorter. Other writers can still queue behind it, and integrity checks or downstream actions can still suffer. If the real complaint is that order entry blocks order entry, applying NOLOCK to a dashboard avoids the central issue.

Find the head of the blocking chain

Capture evidence while blocking is occurring. Identify the waiting statement, the session blocking it, and the head blocker at the top of the chain. Record transaction start time, current and last statement, database, application name, host identity where trustworthy, wait resource, isolation level, and whether the session is sleeping with an open transaction.

Killing a visible blocked session treats the queue. Finding why the head blocker retained locks treats the event.

Common causes include an application transaction that covers user think time, error handling that neglects rollback, a batch modifying too many rows at once, a missing index that makes an update scan, and jobs scheduled into the peak workload. Each has a concrete remedy. Without the chain, the response becomes guesswork.

Reduce the work and transaction duration

  • Keep transactions focused on the statements that must succeed or fail together.
  • Do not open a transaction before waiting for user input or remote service calls.
  • Index selective predicates used by updates and deletes so fewer rows and keys are touched.
  • Process large maintenance changes in controlled batches when the business rules allow it.
  • Ensure every error path commits or rolls back deliberately and returns pooled connections cleanly.

Query tuning helps concurrency even when a reader keeps the default isolation level. A seek that completes in milliseconds generally holds shared locks for less time than a scan that reads the entire table. Be careful, however, not to add a wide index solely for one blocking incident without measuring its cost to the writes already under pressure.

Consider row versioning with full awareness

Read committed snapshot isolation can allow readers using read committed semantics to obtain committed row versions instead of waiting for many data modifications. Snapshot isolation provides explicit transaction-level behavior. These are database design choices, not emergency hints.

Row versioning moves work rather than eliminating it. SQL Server must maintain versions, and long-running readers can extend version retention. Capacity, monitoring, application assumptions, update-conflict handling, and workloads that still require locking all need review. Test representative transactions and reports before changing a production database option.

Distinguish blocking from deadlocks

Blocking is normal coordination: one session waits for another to release a resource. It becomes a problem when the duration or scope violates the service requirement. A deadlock is a cycle in which sessions wait on one another, so SQL Server chooses a victim. The deadlock graph identifies resources and statement order; NOLOCK is not a general deadlock solution.

For repeatable deadlocks, examine access order, indexes, transaction scope, isolation, and retry behavior. Applications should handle a deadlock victim as a transient failure only when retrying the whole logical transaction is safe and bounded.

Make consistency an explicit requirement

There are legitimate cases for approximate, operational views where stale or inconsistent data is understood and harmless. Even then, use a database-level approach or documented reporting architecture when possible so behavior is consistent and reviewable. Scattered hints make it difficult to know which screens can lie and why.

The lasting solution to blocking begins with evidence and the business transaction. Shorten lock duration, improve access paths, correct application boundaries, schedule competing work intelligently, and choose an isolation model that matches the required truth. NOLOCK may hide one wait, but it cannot make an unsafe result correct or an unhealthy transaction design sound.

Working through a similar problem?

Discuss your database question.

Start a conversation