How it works
The explanation, with examples you can run.
Row locks
UPDATE, DELETE and SELECT ... FOR UPDATE lock the rows they touch until the transaction commits or rolls back. Another writer of the same row waits; plain readers do not, they see the last committed version. Variants let you say what you need:
-- Lock the row you are about to change, so nobody changes it under youBEGIN;SELECT balance_cents FROM accounts WHERE id = 1 FOR UPDATE;UPDATE accounts SET balance_cents = balance_cents - 100 WHERE id = 1;COMMIT; -- Fail at once instead of waitingSELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT; -- Work queue: each worker takes a job nobody else holdsSELECT id FROM jobs WHERE state = 'ready'ORDER BY id LIMIT 1FOR UPDATE SKIP LOCKED;FOR SHARE, FOR NO KEY UPDATE and FOR KEY SHARE are weaker modes; foreign key checks use FOR KEY SHARE on the parent row, so inserting an order does not block an update of the customer's name.
Table locks
Every statement takes a table-level lock too, but the ordinary ones (ACCESS SHARE for SELECT, ROW EXCLUSIVE for writes) do not conflict with each other. The one to fear is ACCESS EXCLUSIVE, taken by most forms of ALTER TABLE, DROP, TRUNCATE and VACUUM FULL. It conflicts with everything.
Lock requests wait in a queue. If an ALTER TABLE is waiting behind a long query, every new query on that table queues behind the ALTER, and an instant change takes the site down. Set a timeout for migrations and retry:
SET lock_timeout = '2s';ALTER TABLE orders ADD COLUMN note text;To see who is blocking whom:
SELECT pid, pg_blocking_pids(pid) AS blocked_by, state, left(query, 60)FROM pg_stat_activityWHERE cardinality(pg_blocking_pids(pid)) > 0;Deadlocks
Two transfers, one from account 1 to 2 and one from 2 to 1, started together:
ERROR: deadlock detectedDETAIL: Process 96066 waits for ShareLock on transaction 893; blocked by process 96064.Process 96064 waits for ShareLock on transaction 894; blocked by process 96066.CONTEXT: while updating tuple (0,11) in relation "accounts"A waiting backend checks for a cycle after deadlock_timeout (1 second by default). If it finds one, one transaction is aborted and the others proceed. Which one is not predictable. The fixes are ordinary:
- Take locks in a consistent order: update the lower account id first, whichever direction the money goes.
- Keep transactions short, so the window for a cycle is small.
- Treat a deadlock (SQLSTATE
40P01) like a serialization failure: roll back and retry.
Advisory locks
For coordination that is not about a particular row, such as "only one instance runs this job", pg_advisory_lock(key) and pg_try_advisory_lock(key) take a lock on a number of your choosing. Session-level advisory locks last until released or the connection closes, which matters behind a transaction-mode pooler; pg_advisory_xact_lock is released at the end of the transaction.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.