User Tools

Site Tools


mysql:tx:locking

This is an old revision of the document!


Locks in MySQL

Locks control concurrent access to data during transactions. Without locks, multiple transactions reading and writing the same rows simultaneously can cause dirty reads, lost updates, and write skew.

Read lock (Shared) - FOR SHARE → I am reading, others can read too, but no one can write Write lock (Exclusive) - FOR UPDATE → I am about to update, no one else can touch this row


1 Read Lock (Shared Lock)

Allows multiple transactions to read the same row at the same time, but blocks anyone from writing until all read locks are released.

SELECT * FROM accounts WHERE id = 1 FOR SHARE;

Behavior:

Who Read Write
Same transaction ✅ Yes ❌ No
Other transactions ✅ Yes ❌ Blocked

When to use:

  • You need to read a row and ensure it is not modified while your transaction is still running.
  • Example: generating a financial report — lock the rows being read so no one updates them mid-report.
-- Audit report: lock all accounts in the department
BEGIN;
 
  SELECT id, balance
  FROM accounts
  WHERE department = 'engineering'
  FOR SHARE;
  -- Other transactions can still read these rows
  -- but cannot UPDATE or DELETE them until COMMIT
 
COMMIT;

Key point: Multiple transactions can hold a shared lock on the same row simultaneously. As soon as any transaction wants to write, it must wait for all shared locks to be released.


2. Write Lock (Exclusive Lock)

Blocks other transactions from both reading-with-lock and writing. Plain SELECT (without FOR UPDATE or FOR SHARE) is not blocked.

SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

Behavior:

Who Read Write
Same transaction ✅ Yes ✅ Yes
Other — plain SELECT ✅ Yes
Other — SELECT FOR SHARE ❌ Blocked ❌ Blocked
Other — SELECT FOR UPDATE ❌ Blocked ❌ Blocked
Other — UPDATE / DELETE ❌ Blocked ❌ Blocked

When to use:

  • You need to read a row and then write to it in the same transaction.
  • Example: check balance before deducting — lock the row on read so no one changes it before your write.
-- Debit: lock row, check balance, then deduct
BEGIN;
 
  SELECT balance
  FROM accounts
  WHERE id = 1
  FOR UPDATE;
  -- Row is now exclusively locked
  -- Other FOR UPDATE / FOR SHARE / UPDATE must wait
 
  -- Check balance in application code
  -- If sufficient, proceed:
  UPDATE accounts
  SET balance = balance - 500
  WHERE id = 1;
 
COMMIT;
-- Lock is released here

Important: Only one transaction can hold an exclusive lock on a row at a time. All other write-intent transactions are queued and run one after another.

Plain SELECT is never blocked:

-- While Transaction A holds FOR UPDATE on id=1:
 
-- Transaction B — plain SELECT → goes through immediately
SELECT balance FROM accounts WHERE id = 1;
 
-- Transaction B — FOR UPDATE → blocked, must wait for A
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;

3 Lock Compatibility Matrix

Shows whether two locks can be held simultaneously on the same row.

Held ↓ / Requested → FOR SHARE FOR UPDATE
FOR SHARE ✅ Compatible ❌ Conflict
FOR UPDATE ❌ Conflict ❌ Conflict
  • ✅ Compatible — both transactions proceed at the same time.
  • ❌ Conflict — the requesting transaction is blocked until the held lock is released.

4. Deadlock

Deadlock occurs when two transactions each hold a lock the other needs, causing both to wait forever. MySQL detects this automatically and kills one transaction, returning an error.

Transaction A: locks row id=1, then needs row id=2
Transaction B: locks row id=2, then needs row id=1

Both wait for each other → deadlock

Prevention rule: always lock rows in a consistent order (ascending id).

-- RISK: inconsistent order
-- Transaction A locks id=1 then id=2
-- Transaction B locks id=2 then id=1
-- → deadlock possible
 
-- SAFE: always lock in ascending id order
BEGIN;
  SELECT id, balance
  FROM accounts
  WHERE id IN (1, 2)
  ORDER BY id          -- id=1 first, id=2 second — always
  FOR UPDATE;
 
  UPDATE accounts SET balance = balance - 500 WHERE id = 1;
  UPDATE accounts SET balance = balance + 500 WHERE id = 2;
COMMIT;

5. Table Locks

Lock the entire table instead of individual rows. Rarely used in modern applications — row locks are preferred because table locks block all concurrent access to the whole table.

LOCK TABLES accounts READ;
LOCK TABLES accounts WRITE;
UNLOCK TABLES;

Behavior:

Lock Type Same Session Other Sessions
READ Read only Read only (writes blocked)
WRITE Read + Write Fully blocked

When to use:

  • Bulk imports or exports where you need the table to be completely stable.
  • MyISAM engine (does not support row-level locks).
  • InnoDB always prefers row-level locks — avoid table locks with InnoDB.
-- Example: bulk export, ensure no writes during read
LOCK TABLES accounts READ;
 
  SELECT * FROM accounts;
  -- No other session can INSERT, UPDATE, or DELETE
  -- Other sessions can still SELECT
 
UNLOCK TABLES;

6 Row Lock vs Table Lock

Feature Row Lock (FOR UPDATE / FOR SHARE) Table Lock
Granularity Single row Entire table
Concurrency High — only affected rows locked Low — all rows blocked
Deadlock risk Yes No
Supported engines InnoDB InnoDB, MyISAM
Typical use OLTP, web applications Bulk operations, MyISAM

7. Summary

Lock SQL Blocks writes Blocks FOR SHARE Blocks plain SELECT
Shared (Read) SELECT … FOR SHARE ✅ Yes ❌ No ❌ No
Exclusive (Write) SELECT … FOR UPDATE ✅ Yes ✅ Yes ❌ No
Table Read LOCK TABLES t READ ✅ Yes ❌ No
Table Write LOCK TABLES t WRITE
mysql/tx/locking.1778918417.txt.gz · Last modified: by phong2018