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 UPDATE | Khóa row mạnh cho update/delete và conflicting lockers. |
FOR NO KEY UPDATE | Yếu hơn khi update không đổi key liên quan foreign key. |
FOR SHARE/FOR KEY SHARE | Cho shared/key-reference use cases với conflict khác nhau. |
NOWAIT | Fail ngay thay vì chờ lock. |
SKIP LOCKED | Bỏ 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.
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.
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.