00:00

MySQL Table Locking

Last updated: 6 min read

When many users work with the same data at once, the database must stop them from getting in each other's way. MySQL does this with locks. You can lock whole tables explicitly with LOCK TABLES, and InnoDB also locks individual rows automatically inside transactions.

The examples on this page use two real sessions: Session A and Session B. Session B's lock wait timeout is set to 2 seconds, so instead of waiting forever it reports a timeout error, which shows you exactly when it was blocked.

1. Why Locking Is Needed

Imagine Meera's balance is 5,000. Two cashiers read it at the same time, one adds 1,000 and the other subtracts 500, and each writes back its own result. One update is lost. This is the lost update problem. Locks force the second session to wait until the first has finished.

READ lock versus WRITE lockWith a READ lock, the locking session and other sessions can read, but nobody can write. With a WRITE lock, only the locking session can read and write; other sessions must wait for both reads and writes.LOCK TABLES accounts READLocking sessionOther sessionsread ✔write ✘read ✔write: waitsLOCK TABLES accounts WRITELocking sessionOther sessionsread ✔write ✔read: waitswrite: waits
Figure 1: a READ lock lets everyone read but no one write; a WRITE lock gives one session exclusive access.

2. LOCK TABLES and UNLOCK TABLES

syntax.sql
LOCK TABLES table1 {READ | WRITE} [, table2 {READ | WRITE} ...];
-- ... work with the locked tables ...
UNLOCK TABLES;
  • READ (shared): you and others can read; nobody can write.
  • WRITE (exclusive): only you can read and write; others wait.
  • LOCK TABLES first releases any locks you already hold and commits an open transaction.
  • Locks are released by UNLOCK TABLES, by starting a transaction, or when the session ends.
  • You need the LOCK TABLES privilege and SELECT on the tables.

3. READ Lock in Action

Session A takes a READ lock and can read:

Session A
LOCK TABLES accounts READ;
SELECT * FROM accounts;

Result: 3 rows

acc_noholderbalance
101Meera5000.00
102Rohan3200.00
103Sana7800.00

But Session A can't write to the table it locked for reading:

Session A
UPDATE accounts SET balance = balance + 100 WHERE acc_no = 101;
Output
ERROR 1099 (HY000): Table 'accounts' was locked with a READ lock and can't be updated

Session A also can't touch any table it didn't lock:

Session A
SELECT * FROM branches;
Output
ERROR 1100 (HY000): Table 'branches' was not locked with LOCK TABLES

Session B can still read the locked table:

Session B
SELECT holder, balance FROM accounts WHERE acc_no = 102;

Result: 1 row

holderbalance
Rohan3200.00

But its write has to wait for the lock, and after 2 seconds it times out:

Session B
UPDATE accounts SET balance = 0 WHERE acc_no = 102;
Output
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Session A
UNLOCK TABLES;
Output
Query OK, 0 rows affected

4. WRITE Lock in Action

Session A
LOCK TABLES accounts WRITE;
UPDATE accounts SET balance = balance - 500 WHERE acc_no = 103;
SELECT acc_no, balance FROM accounts WHERE acc_no = 103;

Result: 1 row

acc_nobalance
1037300.00

Now even reading blocks for Session B:

Session B
SELECT * FROM accounts;
Output
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Session A
UNLOCK TABLES;
Output
Query OK, 0 rows affected

After the unlock, Session B reads normally and sees Session A's change:

Session B
SELECT acc_no, balance FROM accounts WHERE acc_no = 103;

Result: 1 row

acc_nobalance
1037300.00

5. Locking Several Tables and Aliases

Lock every table the work needs in one statement. If a query uses an alias, lock the table under that alias too:

multi-lock.sql
LOCK TABLES accounts AS a READ, branches WRITE;
SELECT a.holder FROM accounts AS a WHERE a.balance > 4000;
UPDATE branches SET city = 'Kochi Central' WHERE id = 2;
UNLOCK TABLES;

Result: 2 rows

holder
Meera
Sana

6. InnoDB Row Locks: the Better Tool

LOCK TABLES blocks the whole table. InnoDB transactions instead lock only the rows they change, so different users can update different rows at the same time. Use SELECT … FOR UPDATE to lock rows you are about to change.

Session A locks Meera's row:

Session A
START TRANSACTION;
SELECT balance FROM accounts WHERE acc_no = 101 FOR UPDATE;

Result: 1 row

balance
5000.00

Session B can update a different row immediately:

Session B
UPDATE accounts SET balance = balance + 50 WHERE acc_no = 102;
Output
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0

But changing the same row has to wait, and times out:

Session B
UPDATE accounts SET balance = balance + 50 WHERE acc_no = 101;
Output
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Session A finishes its work and commits, releasing the row lock:

Session A
UPDATE accounts SET balance = balance + 1000 WHERE acc_no = 101;
COMMIT;
SELECT acc_no, balance FROM accounts ORDER BY acc_no;

Result: 3 rows

acc_nobalance
1016000.00
1023250.00
1037300.00
StatementLock taken
SELECT … FOR UPDATEExclusive row locks: others can't change or lock these rows
SELECT … FOR SHAREShared row locks: others can read and share-lock, not change
UPDATE / DELETEExclusive locks on the affected rows (and index gaps in REPEATABLE READ)
Plain SELECTNo locks: reads a consistent snapshot (MVCC)
Add NOWAIT to fail immediately if a row is locked, or SKIP LOCKED to skip locked rows. That is ideal for job queues: SELECT … FOR UPDATE SKIP LOCKED.

7. Metadata Locks

Any statement that uses a table also takes a metadata lock on it, so its structure can't change underneath. An ALTER TABLE must wait until all open transactions that touched the table have finished. A forgotten open transaction can therefore make a quick ALTER TABLE hang. Find blockers with:

find-mdl.sql
SELECT object_name, lock_type, lock_status, owner_thread_id
FROM performance_schema.metadata_locks
WHERE object_schema = 'bank';

8. Deadlocks

A deadlock happens when two transactions each wait for a row the other has locked. InnoDB detects it immediately and rolls back one of them with ERROR 1213 (40001): Deadlock found when trying to get lock. Your application should simply retry that transaction.

TimeSession ASession B
1locks row 101
2locks row 102
3wants row 102 → waits
4wants row 101 → deadlock, B is rolled back
  • Lock rows in the same order everywhere (for example, by ascending acc_no).
  • Keep transactions short.
  • Use indexes so updates lock fewer rows.
  • SHOW ENGINE INNODB STATUS shows the latest deadlock.

9. When to Use LOCK TABLES

  • Rarely, with InnoDB. Transactions and row locks are almost always better.
  • Bulk maintenance on MyISAM tables (which have only table locks).
  • Making a consistent copy of a few non-transactional tables.
  • Never hold table locks while waiting for user input.

10. Summary

  • LOCK TABLES t READ lets everyone read and no one write; WRITE gives one session exclusive access.
  • A session holding table locks can only use the tables it locked; release them with UNLOCK TABLES.
  • InnoDB locks rows inside transactions; use SELECT … FOR UPDATE to lock rows you will change.
  • Metadata locks protect table structure; long transactions can block ALTER TABLE.
  • Deadlocks are detected automatically; retry the failed transaction.