반응형
설치·접속: PostgreSQL 설치와 접속
부제: 계좌이체 두 건이 각각 다른 순서로 두 행을 잠그다가, 서로가 상대의 락을 기다려 영원히 못 끝나는 상황
-- 세션 A: 계좌1 → 계좌2 순서로 잠근다
BEGIN;
UPDATE m7_acct SET balance=balance-10 WHERE id=1; -- 계좌1 락
UPDATE m7_acct SET balance=balance+10 WHERE id=2; -- 계좌2 락 시도 (B가 쥐고 있음)
COMMIT;
-- 세션 B: 계좌2 → 계좌1 순서로 잠근다 (반대!)
BEGIN;
UPDATE m7_acct SET balance=balance-10 WHERE id=2; -- 계좌2 락
UPDATE m7_acct SET balance=balance+10 WHERE id=1; -- 계좌1 락 시도 (A가 쥐고 있음)
COMMIT;
[A] A: 계좌1 잠금 완료
[B] B: 계좌2 잠금 완료
[A] A: 이제 계좌2 잠그려 시도...
[B] B: 이제 계좌1 잠그려 시도...
[A] ERROR: deadlock detected
[A] DETAIL: Process 9900 waits for ShareLock on transaction 929; blocked by process 9901.
[A] Process 9901 waits for ShareLock on transaction 930; blocked by process 9900.
[A] CONTEXT: while updating tuple (0,2) in relation "m7_acct"
[B] B: 계좌1도 잠금 (성공) <- B는 살아남아 정상 진행A는 계좌1을 쥔 채 계좌2를 기다리고, B는 계좌2를 쥔 채 계좌1을 기다린다. 서로가 상대의 락을 기다리므로 그냥 두면 둘 다 영원히 멈춘다. PostgreSQL은 deadlock_timeout(기본 1초)마다 대기 그래프에 순환이 생겼는지 검사하고, 순환을 발견하면 한쪽을 희생자로 골라 40P01 에러로 강제 중단한다. 여기선 A가 죽고 B가 살아 진행했다. 데드락은 락 획득 순서가 트랜잭션마다 엇갈릴 때 생기므로, 근본 해결책은 "항상 id 오름차순으로 잠근다"처럼 잠금 순서를 통일하는 것이다. 그리고 앱은 40P01을 잡으면 트랜잭션을 재시도해야 한다.
이렇게도 쓴다
락 순서를 통일해 데드락 자체를 없앤다(양쪽 다 id 오름차순).
-- 어느 트랜잭션이든 항상 작은 id부터 잠근다
UPDATE m7_acct SET balance=balance-10 WHERE id=LEAST(1,2);
UPDATE m7_acct SET balance=balance+10 WHERE id=GREATEST(1,2);
한 문장으로 두 행을 함께 잠근다(순서 뒤섞임 방지). (조합: IN + ORDER BY)
SELECT id FROM m7_acct WHERE id IN (1,2) ORDER BY id FOR UPDATE;
데드락 감지 주기를 조정한다. (조합: deadlock_timeout)
SET deadlock_timeout = '2s'; -- 짧은 락 대기를 데드락으로 오판하지 않게
앱에서 데드락만 골라 재시도한다. (조합: SQLSTATE 40P01)
-- SQLSTATE '40P01'(deadlock_detected) 잡으면 트랜잭션 재실행
지금 서로 기다리며 막혀 있는 세션을 찾아낸다. (조합: pg_blocking_pids)
SELECT pid, pg_blocking_pids(pid) AS blocked_by, query
FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid)) > 0;
데드락 로그를 서버 로그에 남겨 사후 분석한다. (조합: log_lock_waits)
ALTER SYSTEM SET log_lock_waits = on; -- 대기·데드락 상세를 로그로
SELECT pg_reload_conf();반응형
'명령어 > DB' 카테고리의 다른 글
| [PostgreSQL] GIN vs GiST — 전문검색·배열은 GIN, 범위·좌표·최근접은 GiST (0) | 2026.07.23 |
|---|---|
| [PostgreSQL] 표현식 인덱스(expression index)로 LOWER(email) 검색을 태운다 (0) | 2026.07.23 |
| [PostgreSQL] 확장 통계(CREATE STATISTICS)로 상관 컬럼 추정 오차 잡기 (0) | 2026.07.23 |
| [PostgreSQL] 복합 인덱스 컬럼 순서 — (user_id, status)가 status 단독 조회엔 안 먹히는 이유 (0) | 2026.07.23 |
| [PostgreSQL] BRIN 인덱스로 200만 행 시계열을 24kB로 색인한다 (0) | 2026.07.23 |