락?
- 트랜잭션이 처리되는 순서를 보장하기 위한 방법이다.
- 데이터베이스의 무결성을 유지하는데 목적
- 트랜잭션이 동시에 수행될 떄 데이터 일관성을 해치지 않도록 데이터의 접근을 제어하는 DBMS의 기능
- OS에서 데이터 동기화를 위해 임계 영역에 대한 접근을 제한하는 상호배제기법과 유사
락종류 : 낙관적 락 vs 비관적 락
낙관적 락 Optimistic Lock
- 테이터 충돌이 발생하지 않을것이고 동시 업데이트가 없는 경우에 사용한다.
- 보통 버전 정보를 이용하여 데이터 변경 시점에 충돌을 감지
- 충돌 발생 시 롤백
- 주로 읽기 위주 트랜잭션에서 유리(조회가 많고 수정이 드문 시스템)
예 : JPA > @Version 을 활용한 낙관적 락 처리
비관적 락(Pessimistic Lock)
- 동일 데이터를 동시에 수정할 일이 있다고 보는 것, 동시 업데이트가 빈번한 경우에 사용
- 데이터를 사용하는 동안 다른 트랜잭션의 접근 자체를 차단
- 동시 업데이트가 자주 발생하는 경우 적합
예 : MySQL 비관적 락
BEGIN;
SELECT * FROM orders WHERE id = 123 FOR UPDATE;
COMMIT;
락 유형별 분류 : Shared-lock, Exclusive-lock
공유락 S-lock: 데이터를 읽는 락
- 데이터 읽기용 락
- 여러 트랜잭션이 동시에 공유 락 획득 가능
- 쓰기 불가능, 읽기만 가능
- 데이터를 읽는 연산이므로 데이터의 일관성에 영향을 주지 않아 데이터에 여러 공유 락이 동시에 접근 가능
- 데이터에 대한 사용권을 여러 트랜잭션이 소유가능
- A트랜잭션이 공유락이 걸린 데이터에 공유락을 걸어도 다른 트랜잭션도 공유락 걸린 데이터 사용할 수 있다.
배타락 X-lock : 데이터를 수정하는 락
-
데이터 쓰기용 락
-
오직 한 트랜잭션만 접근 가능
-
다른 트랜잭션은 읽기/쓰기 모두 불가능
-
데이터의일관성을 유지하기 위해 데이터에 하나의 베타 락이 접근 중일떄 다른 베타 락이 접근할 수 있다.
-
배타 락 연산을 실행한 트랜잭션만 해당 데이터에 대한 독점권을 소유
-
A트랜잭션이 배타락걸린 데이터를 사용하고 있다면 다른 트랜잭션은 A트랜잭션이 해당 데이터에 대한 작업이 모두 끝난후에 접근가능
정리
자주 사용하는 락 종류 정리
- 레코드 락 : 특정 행(row)에만 락을 걸어 동시성 제어
- 테이블 락 : 테이블 전체에 락을 걸어 다른 트랜잭션의 접근 차단
- 갭 락 : 존재하지 않는 인덱스 사이(갭)에 락을 걸어 범위 조건 삽입 차단
- 넥스트 키 락 : Row + Gap 락을 동시에 걸어 팬텀 리드 방지
- 낙관적 락 : 버전 필드를 통해 충돌 감지
- 비관적 락 : 수정 전 락을 선점하여 충돌 방지
- Named 락 (Advisory Lock) : 사용자 정의 키 기반 락
- 분산락(Redisson 락) : Redis를 활용한 락, 서버 분산 환경에서 사용
락 예시
- 레코드락 : 특정 주문 ID를 조회하고 수정하는 경우
- 테이블락 : 대량 삭제 또는 테이블 전체 갱신
- 갭락 : 중복 방지(동일한 가격인 데이터 삽입 막기, 범위 조건 삽입 차단)
- 넥스트키 락 : MVCC환경에서 반복 조회 시 레코드 삽입 차단
락 종류가 예상보다 많다. 좀 더 딥하게 알아보자 . 먼저 생소한 넥스트 키락부터
넥스트 키 락
정의와 역할
- Row Lock + Gap Lock을 합친 락
- 리프노드에서 해당 인덱스 레코드와 그 사이의 범위(=갭)까지 락을 건다.
예시로 설명
- 에약 시스템에서 중복 시간대 예약 방지하는 서비스 플로우를 가정하자.
- 한 회의실에 대해 중복 예약이 불가능하도록 구현해야한다.
- 같은 시간대에 예약을 삽입하지 못하게 하기 위해 기존 예약 시간 범위에 락을 걸어야 한다.
- A트랜잭션은 ‘회의실 예약 조회’ + 락 거는 것
- 해당 시간대를 포함하는 기존 예약의 유무를 확인하고
- 해당 시간대(범위)에 next-key lock을 자동으로 설정하여 해당 시간대에 새로운 예약이 삽입되지 못하도록 차단
- B트랜잭션은 A트랜잭션과 동일 시간대(중복 시간대)에 예약을 시도한다.
- B가 겹치는 시간대에 예약을 시도하지만
- A가 갭 락을 포함한 next-key lock을 걸어놨기 때문에
- INSERT는 블로킹되거나 Lock timeout 예외발생
- 이 예시에서 Next-key Lock 의 역할
- 목적 : 특정 시간대에 다른 예약이 삽입되지 못하도록 방지
- 적용조건 : 시간범위에 대한 인덱스가 존재해야 효과적이다. (좀더 효율적으로 처리할 수 있다)
- 격리수준 : REPEATABLE READ로
- 위 격리 수준의 사용목적 : 겹치는 시간 예약은 정합성문제 방지 목적이다.
- 코드
-
InnoDB + REPEATABLE READ + 인덱스 조건 + FOR UPDATE
-
위 4가지 조합이 모두 만족되면 DB엔진이 내부적으로 자동으로 next-key lock을 생성한다.
-
FOR UPDATE : 락 선언 명령, 조회된 레코드에 대해 쓰기락걺
-
InnoDB에서 이때 단순히 해당 레코드에만 락을 거는것이 아니라 팬텀리드를 막기 위해 레코드+갭 에도 락을 건다
START TRANSACTION;
SELECT * FROM reservations
WHERE room_id = 'R1'
AND start_time < '2025-08-08 13:00'
AND end_time > '2025-08-08 12:00'
FOR UPDATE;
===
INSERT INTO reservations (room_id, start_time, end_time)
VALUES ('R1', '2025-08-08 12:30', '2025-08-08 13:30');
적절한 용도
- 넥스트 키 락은
- 예약/스케줄링/좌석/배차 등 "범위 충돌"을 막아야 하는 시스템에서 탁월
- 삽입될 가능성이 있는 존재하지 않는 시간대에도 락을 거는 것이 핵심
- 락을 걸 범위 조건에는 반드시 인덱스가 있어야 성능 및 정확도 확보가 가능하다.
- 대표적인 예시로는 비행기 좌석 예약 (스케줄 충돌 방지 in 항공권 예매 시스템)
*갭?
- 인덱스랑 헷갈리지만 인덱스가 아니다.
- 인덱스 상에서 주어진 레코드사이의 빈 공간, 공간 개념으로 볼때 레코드와 레코드 사이 자체를 갭이라고 하는 것이다.
- 인덱스 구조인 B-tree를 전제로 하며 사용되는 개념이고, 갭은 그 인덱스 구조 안에 정렬된 레코드 사이 비어있는 구간을 말하는 것이다.
- 예를들어 Price 컬럼에서 인덱스가 [10] --- [20] --- [30] --- [40]로 존재한다고 가정할떄
- 이때 레코드는 [10], [20], [30], [40] 이며
- 이때 갭은 10-20, 20-30, 30-40, 40-무한대 공간이다.
결론 :
-
갭(Gap) 이란/ 인덱스 레코드들 사이의 빈 공간(범위)이다.
-
레코드가 20, 30만 존재하면 0-20, 20-30, 30-무한이 갭인 것이다 .
*왜 갭을 막는가?
SELECT * FROM items WHERE price BETWEEN 20 AND 30 FOR UPDATE;
- 실제로 존재하는 [20], [30]에는 Row Lock이 걸림
- 하지만 [25]는 없는데, 누군가 트랜잭션 B에서
price = 25
로 INSERT
하려고 하면? → 갭을 막지 않으면 삽입 가능 → 팬텀 리드 발생
그래서 InnoDB는 아예 [20]과 [30] 사이의 갭
에도 락을 걸어버립니다.
→ 이것이 Next-Key Lock이고, 그 갭이 갭이다.
락 경합으로 인한 문제 : Blocking, Deadlock
블로킹 : 한 트랜잭션이 락을 보유하고 있을 때, 다른 트랜잭션이 해당 리소스를 기다리며 작업이 지연되는 현상으로 장기화되면 병목현상이 나타난다.
- 블로킹을 해소하기 위해서는 이전 트랜잭션이 완료(커밋/롤백)되어야한다.
- 트랜잭션을 짧게 정의해야한다.
- 설계시 같은 데이터를 갱신하는 트랜잭션이 동시에 수행되지 말아야한다.
- 트랜잭션 격리수준을 필요이상으로 높게 설정하면 안된다.
- lock timeout을 이용하여 잠금 해제 시간을 조절하면 된다.
데드락 : 서로가 보유한 락을 서로 기다리면서 영원히 진행되지 못하는 상황으로 A가 B의 자원을 기다리고, B는 A의 지원을 기다리는 구조
- 접근 순서를 정한다.
- 락 획득 전 락 존재 여부 체크
- 트랜잭션을 처리 속도를 최대한 빠르게
- 데드락 방지를 위해 lock timeout을 걸어 락 잠금해제를 설정한다.
- 데드락 감지 기능사용
정리
- 락은 트랜잭션이 처리되는 순서를 보장하여 데이터 무결성을 보장하기 위한 방법이다.
- 락종류로는 공유락, 배타락이 있고
- 공유락은 데이터읽기연산용으로 한 트랜잭션이 수행중일떄 공유락을 가진 다른 트랜잭션도 동시에 접근할 수 있다.
- 반면, 배타락은 데이터를 수정하는 락으로 하나의 트랜잭션을 수행 중일떄 다른 트랜잭션이 접근할 수 없다.
- 락을 이용할 떄는 트랜잭션이 블로킹/데드락에 걸릴수있다는 한계가 존재하며
- 여러 방법을 도입하여 한계를 극복해야한다.
결론
- 락은 데이터 무결성을 지키기 위한 핵심 도구지만, 성능 저하나 데드락과 같은 부작용을 초래할 수 있음
- 시스템 특성과 동시성 요구 수준에 따라 적절한 락 전략을 선택해야 함
- 실무에서는 락 종류를 이해하는 것뿐 아니라, 어떤 시점에 어떤 범위로 락을 거는지가 핵심