Mastering Deadlock Resolution in PostgreSQL: Unleash the Power of SKIP LOCKED
Vineet Stewart Lasrado
@VineetLasrado
@VineetLasrado
Introduction
Behavior with SELECT:
Behavior with UPDATE:
Key Advantage: Reduces blocking and potential deadlocks in high-concurrency scenarios for both reading and writing operations.
@VineetLasrado
Behavior
-- For SELECT statements:�SELECT column1, column2
FROM table_name�WHERE condition�FOR UPDATE SKIP LOCKED [LIMIT count];�
�-- For UPDATE statements:�UPDATE table_name�SET column1 = value1,� column2 = value2,� ...�WHERE condition�SKIP LOCKED [LIMIT count];
@VineetLasrado
Syntax
@VineetLasrado
Use Case of SELECT SKIP LOCK
CREATE TABLE tasks (� id SERIAL PRIMARY KEY,� description TEXT,� status VARCHAR(20) DEFAULT 'PENDING',� priority INTEGER DEFAULT 1�);��INSERT INTO tasks (description, priority) VALUES�('Process high priority email', 1),�('Generate daily reports', 2),�('Update user profiles', 2),�('Analyze website traffic', 3),�('Backup database', 3);
Prepare Two Database Sessions: Open two separate database client connections to your PostgreSQL database (e.g., using psql in separate terminal windows or two query editor tabs in a database management tool like pgAdmin).
@VineetLasrado
Demonstration
Terminal Session 1: In the first database session, execute the following commands to start a transaction and select and lock a pending task:
BEGIN;
SELECT id, description
FROM tasks
WHERE status = 'PENDING'
ORDER BY priority
LIMIT 1
FOR UPDATE;
-- Note the 'id' of the locked task that is returned (e.g., 1).
Terminal Session 2: In the second database session, within a new transaction, try to select and lock a pending task using the same query without SKIP LOCKED:
BEGIN;
�SELECT id, description
FROM tasks
WHERE status = 'PENDING'
ORDER BY priority
LIMIT 1
FOR UPDATE;
Observation: You will likely observe that the query in Session 2 will hang, waiting for the lock held by the transaction in Session 1 to be released. This demonstrates the blocking behavior that can occur when multiple transactions try to lock the same or overlapping sets of rows without SKIP LOCKED.
Session 1: In the first session, commit the transaction to release the lock:
COMMIT;
Observation: Once the transaction in Session 1 is committed, the query in Session 2 will likely proceed, selecting and locking a pending task.
@VineetLasrado
Demonstration - Part 1 (Without SKIP LOCKED - SELECT FOR UPDATE)
Reset the tables: UPDATE tasks SET status = 'PENDING';
Terminal Session 1: In the first session, execute the SELECT ... FOR UPDATE SKIP LOCKED statement:
BEGIN;�SELECT id, description �FROM tasks �WHERE status = 'PENDING' �ORDER BY priority LIMIT 1 �FOR UPDATE SKIP LOCKED;�-- Note the 'id' of the selected and locked task.��SELECT * FROM tasks �WHERE id = (SELECT id FROM tasks WHERE status = 'PENDING' ORDER BY priority LIMIT 1 FOR UPDATE SKIP LOCKED);�-- DO NOT COMMIT this transaction yet.
Terminal Session 2: In the second database session, execute the same SELECT ... FOR UPDATE SKIP LOCKED statement
BEGIN;�SELECT id, description FROM tasks �WHERE status = 'PENDING' �ORDER BY priority LIMIT 1 �FOR UPDATE SKIP LOCKED;�-- Note the 'id' of the selected and locked task in this session. It will likely be a different task if one was successfully locked in Session 1.��SELECT * FROM tasks �WHERE id = (SELECT id FROM tasks WHERE status = 'PENDING' ORDER BY priority LIMIT 1 FOR UPDATE SKIP LOCKED);�COMMIT;
@VineetLasrado
Demonstration - Part 2 (With SKIP LOCKED - SELECT FOR UPDATE SKIP LOCKED)
Terminal Session 1: Now, commit the transaction in the first session:
COMMIT;
Observation: You should observe that both sessions were able to successfully select and lock different pending tasks without blocking each other. The SKIP LOCKED clause in each session allowed it to bypass any rows that were already locked by the other session, enabling concurrent access to available tasks.
@VineetLasrado
Demonstration - Part 2 (With SKIP LOCKED - SELECT FOR UPDATE SKIP LOCKED)
@VineetLasrado
When to Use SKIP LOCKED?
Benefits of SKIP LOCKED
Here
SCAN
@hackersmang