[비관적 락 (Pessimistic)]
"충돌이 날 거야" → 미리 잠금
TX1: SELECT ... FOR UPDATE ← 락 획득
TX2: SELECT ... FOR UPDATE ← 대기 (블로킹)
TX1: UPDATE → COMMIT ← 락 해제
TX2: 이제 실행 가능
[낙관적 락 (Optimistic)]
"충돌 안 날 거야" → 나중에 확인
TX1: SELECT (version=1)
TX2: SELECT (version=1)
TX1: UPDATE WHERE version=1 → OK (version=2)
TX2: UPDATE WHERE version=1 → 실패! (이미 2)
→ 재시도 필요
비교
항목
비관적 락
낙관적 락
방식
DB 락 (SELECT FOR UPDATE)
버전 체크 (WHERE version=N)
충돌 시
대기 (블로킹)
실패 → 재시도
성능
충돌 많으면 유리
충돌 적으면 유리
데드락
가능
불가능
적합한 경우
좌석 예약, 재고 차감
게시글 수정, 설정 변경
실무 구현
-- 비관적 락: 재고 차감
BEGIN;
SELECT stock FROM products
WHERE id = 1 FOR UPDATE; -- 락!
UPDATE products
SET stock = stock - 1
WHERE id = 1;
COMMIT;
-- 낙관적 락: 게시글 수정
UPDATE posts
SET title = '새 제목',
version = version + 1
WHERE id = 1
AND version = 5; -- 버전 체크
-- affected_rows = 0이면 충돌!
선택 기준
충돌 빈도 높음 (재고, 좌석)
→ 비관적 락 (대기가 재시도보다 저렴)
충돌 빈도 낮음 (프로필, 설정)
→ 낙관적 락 (락 없이 성능 좋음)
분산 환경 (여러 서버)
→ 낙관적 락 선호 (DB 락 범위 제한)
면접에서 이렇게 나옵니다
Q.낙관적 락과 비관적 락의 차이를 설명해주세요
충돌을 미리 막는지, 나중에 감지하는지가 다릅니다.
항목
비관적 락
낙관적 락
전제
충돌이 자주 난다
충돌이 드물다
방법
읽을 때부터 잠근다
버전을 비교해 갱신 시 확인한다
구현
SELECT ... FOR UPDATE
version 컬럼이나 갱신 시각 비교
대기
있다. 다른 트랜잭션이 기다린다
없다
실패 처리
거의 없다
충돌 시 재시도하거나 사용자에게 알린다
위험
데드락, 대기 누적
재시도 폭증
기준은 충돌 빈도입니다. 충돌이 드물면 낙관적이 훨씬 빠르고, 잦으면 재시도 비용이 잠금 대기 비용을 넘습니다.
흔한 실수: 낙관적 락을 "잠금을 안 쓰니 항상 빠르다"고 답하는 것. 갱신하는 순간에는 DB 가 행을 잠급니다. 다른 점은 읽는 동안 잠그지 않는다는 것입니다.
Q.쇼핑몰 재고 차감에 어떤 락 방식이 적합한가요? 이유는?
경쟁 정도에 따라 다릅니다. 하나로 답하면 반쯤 틀립니다.
상황
적합한 방식
일반 상품. 초당 주문 0.2건
낙관적 락. 충돌이 사실상 없다
한정 판매. 초당 800건이 재고 100개로 몰린다
비관적 락이나 원자적 감소
한정 판매에서 낙관적 락을 쓰면 대부분이 충돌해 재시도가 폭증하고, 재시도가 다시 충돌을 키웁니다.
실무에서 자주 쓰는 방법은 조건부 갱신입니다.
UPDATE stock SET qty = qty - 1 WHERE id = ? AND qty > 0
-- 영향받은 행이 0이면 재고 부족
읽고 계산해서 쓰는 것이 아니라 한 문장으로 조건과 갱신을 함께 하므로 경쟁 상태가 생기지 않습니다. 극단적인 경쟁이면 재고를 캐시에서 원자적으로 차감하고 DB 는 나중에 정산하기도 합니다.
흔한 실수: 읽고 확인한 뒤 갱신하는 흐름을 그대로 두고 락만 논하는 것. 문장 하나로 합치는 것이 더 근본적인 해결입니다.
Q.낙관적 락에서 충돌이 발생하면 어떻게 처리하나요?
갱신 시 영향받은 행이 0이면 충돌입니다. 그 다음 처리를 정해야 합니다.
처리
언제
자동 재시도
읽고 다시 계산해도 결과가 타당할 때. 재고 차감, 카운터
사용자에게 알림
사람이 판단해야 할 때. 문서 동시 편집
병합
서로 다른 필드를 고쳤다면 합칠 수 있다
마지막 쓰기 우선
정확성이 덜 중요한 값. 조회수, 마지막 접속 시각
자동 재시도를 쓸 때는 상한과 대기 시간이 필요합니다.
3회까지, 50ms 100ms 200ms 로 늘려가며
그래도 실패하면 사용자에게 알린다
간격을 늘리는 이유는 같은 간격으로 재시도하면 또 같은 시점에 부딪히기 때문입니다.
흔한 실수: 무한 재시도를 두는 것. 경쟁이 심할 때 서버 부하가 폭증해 상황을 악화시킵니다.