Part 04 · PostgreSQL & Redis · 4.1.04

Locking và concurrent transactions

MVCC giảm reader-writer blocking nhưng không xóa locks. Correctness phụ thuộc lock mode, acquisition order, transaction duration và cách application xử lý conflicts như một outcome bình thường.


Table-level locks

PostgreSQL có nhiều table lock modes với conflict matrix. DML thường lấy ROW EXCLUSIVE; DDL và một số maintenance operations cần mode mạnh hơn. Tên có “ROW” không có nghĩa chỉ khóa rows. Migration có thể chờ lock rồi chặn traffic phía sau, nên cần lock_timeout, statement timeout và rollout plan.

Row-level locks

Mode/optionÝ nghĩa thực tế
FOR UPDATEKhóa row mạnh cho update/delete và conflicting lockers.
FOR NO KEY UPDATEYếu hơn khi update không đổi key liên quan foreign key.
FOR SHARE/FOR KEY SHARECho shared/key-reference use cases với conflict khác nhau.
NOWAITFail ngay thay vì chờ lock.
SKIP LOCKEDBỏ rows đang locked; hữu ích cho competing queue workers, không cho consistent general read.

Row locks không chặn plain SELECT, nhưng chặn writers/lockers tương ứng. Query cần index phù hợp và transaction ngắn để tránh scan/lock nhiều rows hoặc giữ lock quá lâu.

Optimistic concurrency

Dùng version column hoặc compare-and-set SQL: UPDATE ... WHERE id=? AND version=?. Affected-row count 0 nghĩa conflict hoặc stale state. Mô hình này hợp khi conflict thấp và retry rẻ; retry toàn use case với fresh state thay vì chỉ lặp câu UPDATE.

Atomic conditional update

Một invariant đơn giản thường được bảo vệ rõ nhất bằng một statement, tránh application read-modify-write race:

UPDATE account
SET balance = balance - :amount
WHERE id = :id
  AND balance >= :amount;

Affected-row count bằng 1 nghĩa debit thành công; bằng 0 nghĩa account không tồn tại hoặc balance không đủ và application phân loại outcome. Constraint CHECK (balance >= 0) vẫn là defense-in-depth cho mọi writer.

Pessimistic concurrency

Lock row trước decision để serialize contenders. Nó phù hợp khi conflict cao hoặc action khó retry, nhưng làm tăng wait, deadlock risk và connection occupancy. lock_timeout giới hạn thời gian chờ lock; statement_timeout giới hạn toàn statement, hai loại không đồng nghĩa.

Deadlock

PostgreSQL phát hiện wait cycle sau deadlock_timeout và abort một transaction. Phòng ngừa bằng global lock ordering, sort keys trước batch lock, transaction ngắn và không chờ remote systems. Bật/log deadlock graph để xác định statements và resources trong cycle.

Ví dụ ordering: mọi transfer luôn khóa account có ID nhỏ trước ID lớn. Nếu mỗi request khóa “from rồi to”, hai transfer ngược chiều có thể tạo cycle.

Advisory locks

Advisory lock dùng application-defined integer keys và có session hoặc transaction scope. Database không biết resource semantics, nên key mapping/collision và pooled-session lifecycle rất quan trọng. Không thay constraint hoặc row lock nếu resource đã được biểu diễn rõ trong tables.

Serializable và retry policy

Serialization failure và deadlock victim là expected concurrency outcomes. Retry phải bounded, có jitter, shared deadline và idempotency; mỗi attempt mở transaction mới và đọc fresh state. Không retry unique violation, authorization hoặc validation failure mù quáng.

Operational red flag: transaction idle in transaction vẫn giữ snapshot và có thể giữ locks. Theo dõi transaction age, wait events, blockers và application traces; kill session chỉ là mitigation, không phải root-cause fix.
Nguồn tham khảo