Skip to Content

12장. 자물쇠를 채우다 — 락

만화로 보는 요약 — 먼저 읽어보세요

만화로 보는 요약 — 먼저 읽어보세요

락은 내가 쓰는 동안 남을 기다리게 하는 가장 직관적인 격리 장치다 → S락은 읽기끼리 공존하고 X락은 배타적으로 줄 세운다 → 2PL은 직렬성을 보장하지만 느림과 데드락 비용이 있다.

면접 실전 질문: ① S락과 X락의 차이 ② 2단계 잠금이 직렬성을 보장하는 이유 ③ 비관적 동시성 제어의 장단점


배경 — 이제 “어떻게”를 연다

제3부까지 우리는 격리가 무엇을 보장하는지 봤어요. 유령들, 격리 수준, 스냅샷. 그런데 정작 DB가 그걸 어떻게 해내는지는 안 봤습니다. 제4부는 그 내부로 들어갑니다. 가장 직관적인 장치부터요. 자물쇠(lock).

발상은 단순해요. “내가 건드리는 동안 남이 못 건드리게 잠근다.” 7장에서 “가장 단순한 방법은 잠그는 것”이라고 흘렸던 그 방법이죠. 이 장에서 그 자물쇠의 종류와 규칙을 봅니다.

스토리 — 멈춰 선 커서

창을 두 개 열어봅시다(세션 A, 세션 B). A가 계좌 42를 수정하려고 자물쇠를 걸며 읽습니다.

-- [세션 A] BEGIN; SELECT * FROM account WHERE id = 42 FOR UPDATE; -- 이 행에 쓰기 자물쇠를 건다
-- [세션 B] 같은 행을 건드리려 하면? BEGIN; SELECT * FROM account WHERE id = 42 FOR UPDATE; -- ⏳ 커서가 멈춘다... A가 끝날 때까지 대기
-- [세션 A] UPDATE account SET balance = balance - 100 WHERE id = 42; COMMIT; -- 락 해제 → 그 순간 B가 깨어나 진행

세션 B의 커서가 멈춰 있던 그 몇 초 — 그게 락의 정체예요. A가 쓰는 동안 B를 기다리게 한 겁니다.

그런데 모든 걸 이렇게 막으면 너무 느려요. 읽기만 하는 사람끼리도 기다려야 할까요? 아니죠. 그래서 락은 두 종류로 나뉩니다.

  • 공유 락(Shared Lock, S락) = “읽기 자물쇠”. 여러 명이 동시에 읽는 건 괜찮죠. 아무도 안 바꾸니까. 그래서 S락은 여러 개가 같이 걸릴 수 있어요.
  • 배타 락(Exclusive Lock, X락) = “쓰기 자물쇠”. 쓰는 동안엔 아무도 읽지도 쓰지도 못하게 해야 합니다. 그래서 X락은 혼자만 걸립니다(위 세션 B가 막힌 이유).

궁합을 표로 보면 이래요. (걸려 있는 락 vs 새로 걸려는 락)

상대가 S락상대가 X락
S락 요청가능(같이 읽기)대기
X락 요청대기대기

요점은 하나예요. 읽기끼리는 공존, 쓰기가 끼면 무조건 줄서기. 여러 명이 같은 데이터를 읽는 건 자유지만, 한 명이라도 쓰려 하면 나머지는 기다려야 합니다.

핵심 — 2단계 잠금과 비관적 제어

락을 아무렇게나 걸었다 풀면 격리가 깨질 수 있어요. 그래서 규칙이 필요합니다. 그게 2단계 잠금(Two-Phase Locking, 2PL) 이에요. 이름 그대로 트랜잭션을 두 국면으로 나눕니다.

  1. 확장 국면(growing): 필요한 락을 얻기만 한다. 푸는 건 없음.
  2. 수축 국면(shrinking): 한번 풀기 시작하면 얻지는 않고 풀기만 한다.

쉽게 말해 “필요한 자물쇠를 다 모을 때까지 하나도 안 푼다” 예요. 보통은 트랜잭션이 끝날 때(COMMIT/ROLLBACK) 한꺼번에 풉니다. 이 규칙을 지키면 직렬성이 보장돼요. 직관은 이렇습니다. 락을 커밋까지 안 놓으니, 남이 내 중간 상태를 절대 못 봅니다. 그래서 순서가 강제되고 = 한 명씩 처리한 것과 같아지죠(3장의 직렬성).

실무에서 락을 직접 거는 대표 문법이 방금 본 FOR UPDATE(X락)와 FOR SHARE(S락)예요. JPA에서는 애노테이션으로 같은 걸 합니다.

interface AccountRepository extends JpaRepository<Account, Long> { @Lock(LockModeType.PESSIMISTIC_WRITE) // = SELECT ... FOR UPDATE Optional<Account> findById(Long id); }

이렇게 “일단 잠그고 본다”는 접근을 비관적 동시성 제어(pessimistic concurrency control) 라고 불러요. “어차피 충돌 날 거야, 미리 막자”는 비관론이죠. 정확하지만 대가가 있습니다. 남을 기다리게 하니 느리고, 잘못하면 서로를 기다리다 영영 멈춥니다. 그 “영영 멈춤”이 바로 데드락인데, 16장에서 정면으로 다룹니다.

정리

  • 격리를 구현하는 가장 직관적 장치 = 락(자물쇠). “내가 쓸 동안 남이 못 건드리게.”
  • 두 종류: S락(읽기, 공유 가능) / X락(쓰기, 배타적). 읽기끼리는 공존, 쓰기가 끼면 대기.
  • 2단계 잠금(2PL) = “필요한 락을 다 모을 때까지 안 푼다(보통 커밋 때 해제)” → 직렬성 보장.
  • 직접 거는 문법: SELECT ... FOR UPDATE(X) / FOR SHARE(S), JPA @Lock. 이런 “일단 잠그고 본다” = 비관적 동시성 제어.
  • 대가: 느림 + 데드락 위험(16장).

생각해볼 질문: 락은 확실하지만 기다리게 합니다. 그런데 11장에서 스냅샷은 “아무도 안 기다린다”고 했죠. 잠그지도 않는데 어떻게 격리가 되는 걸까요? 그 “여러 벌의 사진”은 대체 어떻게 만들어지는 걸까요? (13장, MVCC로 갑니다.)

13장 · 시간을 여러 겹으로 — MVCC

Last updated on