Script Valley
PostgreSQL: Complete Course
Transactions, Concurrency, and LockingLesson 5.3

How PostgreSQL row locking works: SELECT FOR UPDATE and advisory locks

SELECT FOR UPDATE, SELECT FOR SHARE, NOWAIT SKIP LOCKED, row-level vs table-level locks, advisory locks, deadlock detection, lock_timeout

Locking in PostgreSQL

Explicit row locking is necessary when you read a row with intent to update it — preventing another transaction from modifying it between your read and write.

SELECT FOR UPDATE

BEGIN;
-- Lock the row for exclusive update
SELECT id, balance
FROM   accounts
WHERE  id = 42
FOR    UPDATE;

-- Now safely update without race condition
UPDATE accounts SET balance = balance - 100 WHERE id = 42;
COMMIT;

SKIP LOCKED — for job queues

SELECT id, payload
FROM   job_queue
WHERE  status = 'pending'
ORDER  BY created_at
LIMIT  1
FOR    UPDATE SKIP LOCKED;

SKIP LOCKED skips rows already locked by another transaction, enabling multiple workers to pull from a queue concurrently without blocking.

Advisory locks

SELECT pg_advisory_lock(12345);   -- application-level lock
-- do work
SELECT pg_advisory_unlock(12345);

Advisory locks are application-level named locks not tied to any row or table. Use them for distributed mutual exclusion (e.g. prevent two app servers from running the same cron job simultaneously). Set lock_timeout = '5s' to avoid indefinite blocking.

Up next

What is MVCC in PostgreSQL and how does it affect performance

Sign in to track progress