Zettelkasten

InnoDB는 Next-Key Lock으로 팬텀 리드를 방지한다

·수정 2026.04.23·수정 2

요약

  • Next-Key Lock은 Record Lock + Gap Lock의 조합으로, 레코드와 그 앞의 간격을 함께 잠근다
  • InnoDB의 REPEATABLE READ 격리 수준에서 팬텀 리드를 방지하는 핵심 메커니즘이다

본문

잠금 종류 비교

잠금 타입 대상 용도
Record Lock 인덱스 레코드 자체 특정 행 보호
Gap Lock 레코드 사이 간격 새 행 삽입 방지
Next-Key Lock 레코드 + 앞 간격 팬텀 리드 방지

Record Lock

인덱스 레코드 자체에만 잠금을 건다.

-- id=10인 레코드만 잠금
SELECT * FROM users WHERE id = 10 FOR UPDATE;

Gap Lock

인덱스 레코드 사이의 간격을 잠근다. 해당 간격에 새로운 행 삽입을 방지한다.

-- 예: id가 10, 20인 레코드가 있을 때
SELECT * FROM users WHERE id = 15 FOR UPDATE;
-- id 10~20 사이 간격에 Gap Lock
-- 다른 트랜잭션이 id=12, 15, 18 등 삽입 불가

Next-Key Lock

Record Lock + Gap Lock 조합. 레코드 자체와 그 앞쪽 간격을 함께 잠근다.

인덱스: ... | 10 | 20 | 30 | ...

id=20에 Next-Key Lock 시:
- (10, 20] 범위 잠금 (10 초과 ~ 20 이하)

팬텀 리드 방지 원리

-- 트랜잭션 A
BEGIN;
SELECT * FROM orders WHERE amount > 100 FOR UPDATE;
-- amount > 100 범위에 Gap Lock 설정

-- 트랜잭션 B (대기 발생)
INSERT INTO orders (amount) VALUES (150);  -- 블록됨!

-- 트랜잭션 A
SELECT * FROM orders WHERE amount > 100 FOR UPDATE;
-- 같은 결과 보장 (팬텀 리드 없음)
COMMIT;

잠금 범위 예시

인덱스에 10, 20, 30이 있을 때:

쿼리 잠금 범위
WHERE id = 20 (10, 20] Next-Key Lock
WHERE id = 25 (20, 30) Gap Lock
WHERE id > 15 (10, 20], (20, 30], (30, +∞)

주의사항

  • Gap Lock은 REPEATABLE READ 이상에서만 동작
  • 유니크 인덱스로 단일 행 조회 시 Gap Lock 없이 Record Lock만 사용
  • Gap Lock끼리는 충돌하지 않음 (둘 다 삽입 방지 목적)
  • 범위 조회 시 예상보다 넓은 범위가 잠길 수 있음

참고