🔒 Free tier data may be used to improve AI models. Upgrade Pro for 100% Privacy

SQL Server and the Story of Locks — Does a Transaction Lock the Whole Table?

SQL Server and the Story of Locks — Does a Transaction Lock the Whole Table?

2025-11-08 18:45 | 8 min read | 291 views | Author: Thai Nguyen (Software Engineer)

When I first learned SQL, I was just like you — barely passed the class, only remembering things like SELECT *, INNER JOIN, WHERE, and GROUP BY.

But when I started building real web applications, especially backend systems in .NET, I realized something important: SQL isn’t just a query language. It determines whether your system runs smoothly or gets stuck, whether your data stays accurate or turns into chaos.

And among all of its hidden mechanics, one of the most overlooked yet crucial features is Locking — the mechanism SQL Server uses to control how data is read and written simultaneously.


⚙️ 1. What Is a Lock?

A lock is basically a temporary “hold” that SQL Server places on data when you read, update, or delete it.
The goal is simple: prevent two people from changing the same row at the same time, which could cause inconsistent or incorrect data.
When multiple queries access the same data concurrently, SQL Server uses locks to:
  1. 🔒 Keep data consistent.
  2. 🔄 Ensure isolation between transactions — so they don’t step on each other.
  3. 🚫 Avoid race conditions where two sessions overwrite each other’s changes.


📚 2. Main Types of Locks

SQL Server provides several types of locks depending on the operation being performed:
  1. Shared (S) — used when reading data (SELECT).
  2. Exclusive (X) — used when modifying data (INSERT, UPDATE, DELETE).
  3. Update (U) — used to avoid deadlocks when reading before updating.
  4. Intent locks (IS, IX) — indicate that lower-level locks exist on rows or pages.
  5. Schema locks — used when changing the database structure (e.g., ALTER TABLE).


🧩 3. Lock Granularity

SQL Server can apply locks at different levels of granularity — meaning how much data is being locked at once:
  1. 🔸 Row Lock — locks a single row.
  2. 🔸 Page Lock — locks one page (~8 KB, usually containing several rows).
  3. 🔸 Table Lock — locks the entire table.
  4. 🔸 Database Lock — extremely rare, used when modifying the whole database.
SQL Server automatically decides which lock level to use:
  1. When a query affects only a few rows → it uses ROWLOCK to minimize overhead.
  2. When a query affects many rows → it may escalate to a PAGE LOCK or even a TABLE LOCK to reduce the cost of managing too many individual locks.
⚠️ If you ever notice that “the whole table seems to be locked” in your logs, it’s probably not a bug — it’s just lock escalation, where SQL Server automatically upgrades the lock level for efficiency.


🔄 4. Real-World Example — Why Locks Can Slow Things Down

Example 1: NOLOCK

Let’s start simple.

Suppose we create a table and insert two rows of data:

create table UserBalance(
Id int identity(1,1) primary key,
Balance MONEY default 0
)
insert into UserBalance(Balance) values (100000), (900000)
select * from UserBalance

The situation: you need to fetch the UserBalance list, but thousands of users are running balance updates at the same time — countless sessions are hitting the table concurrently.

Meaning, you have to wait for all those transactions to finish and commit before your query can return any data — that’s when you start to see blocking and delays.

Giải pháp dùng with(nolock)

select * from UserBalance with(nolock)

When one session is running an UPDATE, SQL Server places an Exclusive Lock (X) on that row. This means other sessions can only read it after the first transaction has committed.

The more transactions waiting on each other, the more your system starts to hang — leading to timeouts, queue build-ups, and overall slowness.

That’s why developers who don’t understand locking often end up writing code that slows down the entire system — even though their queries look “simple.”

Using WITH (NOLOCK) can make things super fast (and it’s used in most SELECT queries for that reason), but it comes with a big risk.

For example, imagine Session A updates a user’s balance to 0 but hasn’t committed yet. Another session reads that data with NOLOCK and thinks the user is broke, so it triggers a payment-error message.

A second later, Session A rolls back the transaction — the real balance is still 100 000.

But the other session has already raised a false error: “You’re out of money!”


Example 2: UPDLOCK

Now imagine two sessions both trying to deduct money at the same time.

Let’s say you only want to subtract funds if the user’s balance is ≥ 100 000 (and currently it is).

You first SELECT the balance to check the condition, then UPDATE the record if it passes:

begin tran update_balance
declare @moeny money = (select balance from UserBalance where id = 1)
print(@moeny)
if(@moeny >= 100000)
begin
update UserBalance set balance-=100000 where id = 1
end


When both sessions run at the same time, they both see the same balance — 100 000.

This means both satisfy the condition @money >= 100000 and both proceed to the UPDATE.

The expected result should be a remaining balance of 0, with one transaction failing due to insufficient funds.

But instead, both succeed, and the balance becomes -100 000 — clearly wrong.

👉 The fix: simply add WITH (UPDLOCK) in the SELECT statement.

This forces Session 2 to wait until Session 1 finishes before reading the balance.

That way, only one transaction can pass the condition and update at a time — no more double spending.

begin tran update_balance
declare @moeny money = (select balance from UserBalance with(updlock) where id = 1)
print(@moeny)
if(@moeny >= 100000)
begin
update UserBalance set balance-=100000 where id = 1
end


🔍 5. Common Hints to Control Locking

SQL Server lets you explicitly control how locking works using table hints — small directives you can add after a table name to tell the engine how to handle locks.

ví dụ:

Choosing the right hint depends on your use case:

  1. Use NOLOCK for read-only reports where speed matters more than perfect accuracy.
  2. Use UPDLOCK when reading before updating to avoid race conditions.
  3. Use ROWLOCK when updating only a few rows and you want to reduce blocking.


🧠 6. Deadlock — When Two Sessions Wait Forever

A deadlock happens when two (or more) transactions each hold a lock on a resource, and both are waiting for the other to release it.
In other words: A is waiting for B, and B is waiting for A — forever.


➡️ Both transactions end up waiting for each other, so SQL Server has to terminate one to break the deadlock.
Understanding how UPDLOCK works helps you avoid this situation entirely.

💬 Conclusion

Locks in SQL Server are not bugs — they’re features.
The key is understanding how they work so you can:
  1. Avoid unnecessary blocking.
  2. Know when to use NOLOCK or UPDLOCK.
  3. Write transactions that are safe, consistent, and high-performance.
Once you truly understand locking, your SQL code won’t just “work” — it’ll scale smoothly under real-world load.

Frequently Asked Questions

Q: What is a Lock in SQL Server?

A: A lock is a temporary mechanism that SQL Server uses to control access to data when multiple users read or modify it at the same time. Its purpose is to prevent two transactions from changing the same row simultaneously, ensuring consistency and isolation.

Q: What are the main types of locks in SQL Server?

A: SQL Server provides several lock types: Shared (S) for SELECT, Exclusive (X) for INSERT/UPDATE/DELETE, Update (U) to prevent deadlocks, Intent locks (IS/IX) to indicate lower-level locks, and Schema locks used when modifying table structure.

Q: What is lock granularity in SQL Server?

A: Lock granularity defines how much data SQL Server locks at once: Row Lock (1 row), Page Lock (8KB page), Table Lock (entire table), and Database Lock (rare, for full database operations). SQL Server can automatically escalate locks for efficiency.

Q: What does NOLOCK do and what are its risks?

A: NOLOCK lets you read data without waiting for other transactions to finish, improving performance but allowing dirty reads. It can return uncommitted or rolled-back data, which may cause inconsistencies.

Q: How is UPDLOCK different from NOLOCK?

A: UPDLOCK places an update lock during a SELECT operation, preventing other transactions from reading or updating the same rows until the first transaction completes. This helps avoid race conditions and deadlocks.

Q: What is the purpose of ROWLOCK?

A: ROWLOCK forces SQL Server to lock only specific rows instead of pages or the entire table. It increases concurrency for small updates but can use more memory when many rows are locked simultaneously.

Q: What is a deadlock in SQL Server?

A: A deadlock occurs when two or more transactions hold locks on separate resources and wait for each other to release them. SQL Server detects the cycle and terminates one transaction to resolve the deadlock.

Q: How can you prevent deadlocks?

A: Maintain a consistent order when accessing resources, use UPDLOCK to reserve rows early, keep transactions short, and add retry logic in your code to safely handle deadlock victims.

Q: When should I use NOLOCK, UPDLOCK, or ROWLOCK?

A: Use NOLOCK for read-only reports where speed matters more than accuracy, UPDLOCK when reading before updating, and ROWLOCK for small updates where you want to reduce blocking.

Q: Are locks in SQL Server considered errors?

A: No. Locks are built-in features that ensure data consistency and concurrency control. Problems occur only when developers misunderstand how locks work, leading to blocking or performance issues.

Was this article helpful?

Latest from Our Blog

Không có bài viết nào