Hai cách xử lý khi nhiều người cùng sửa một dòng — khác nhau ở chỗ "khóa trước" hay "kiểm tra sau":
Pessimistic (bi quan, giả định sẽ xung đột): khóa dòng ngay khi đọc bằng SELECT ... FOR UPDATE, người khác phải chờ tới khi commit/rollback. An toàn nhưng giảm song song và có rủi ro deadlock nếu khóa không theo thứ tự nhất quán.
Optimistic (lạc quan, giả định ít đụng): không khóa, thêm cột version; khi cập nhật:
UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?;Nếu 0 dòng bị ảnh hưởng → có người sửa trước mình rồi → retry. Hợp tình huống đọc nhiều, ít tranh chấp.
Vài kỹ thuật PostgreSQL đáng nhớ:
- SELECT ... FOR UPDATE SKIP LOCKED: bỏ qua dòng đang bị khóa — pattern chuẩn cho job queue, tránh nhiều worker nhặt trùng việc.
- Advisory lock: pg_try_advisory_lock(key) — khóa ở tầng app, hợp điều phối phân tán (vd chỉ một cron chạy một lúc).
- Đặt lock_timeout để query không chờ khóa vô tận.
Two ways to handle many people editing the same row — differing in "lock first" vs "check later":
Pessimistic (assumes conflicts will happen): lock the row on read with SELECT ... FOR UPDATE; others wait until you commit/rollback. Safe but reduces concurrency and risks deadlock if locks aren't taken in a consistent order.
Optimistic (assumes conflicts are rare): no locking, add a version column; on update:
UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?;If 0 rows are affected → someone edited it before you → retry. Good for read-heavy, low-contention cases.
A few PostgreSQL techniques worth remembering:
- SELECT ... FOR UPDATE SKIP LOCKED: skips already-locked rows — the standard job-queue pattern, preventing multiple workers from grabbing the same job.
- Advisory locks: pg_try_advisory_lock(key) — app-level locks, good for distributed coordination (e.g. only one cron runs at a time).
- Set lock_timeout so queries don't wait for a lock forever.