[DB] Lock : 낙관적락, 비관적락,넥스트키락, S락, X락, 그 외 다양한 락과 락경합 문제

[verify$y]·2025년 8월 16일
0

CS핵심개념

목록 보기
31/35

락?

  • 트랜잭션이 처리되는 순서를 보장하기 위한 방법이다.
  • 데이터베이스의 무결성을 유지하는데 목적
  • 트랜잭션이 동시에 수행될 떄 데이터 일관성을 해치지 않도록 데이터의 접근을 제어하는 DBMS의 기능
  • OS에서 데이터 동기화를 위해 임계 영역에 대한 접근을 제한하는 상호배제기법과 유사


락종류 : 낙관적 락 vs 비관적 락

낙관적 락 Optimistic Lock

  • 테이터 충돌이 발생하지 않을것이고 동시 업데이트가 없는 경우에 사용한다.
  • 보통 버전 정보를 이용하여 데이터 변경 시점에 충돌을 감지
  • 충돌 발생 시 롤백
  • 주로 읽기 위주 트랜잭션에서 유리(조회가 많고 수정이 드문 시스템)

예 : JPA > @Version 을 활용한 낙관적 락 처리

  • JPA 낙관적 락
    @Entity
    public class Order {
        @Version
        private Long 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에서 이때 단순히 해당 레코드에만 락을 거는것이 아니라 팬텀리드를 막기 위해 레코드+갭 에도 락을 건다

      --A트랜잭션이다. 
      --넥스트키락은 FOR UPDATE을 사용하는 순간 자동으로 동작한다. 
      
      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;
      
      ===
      --B트랜잭션
      -- 동시에 다른 사용자가 겹치는 시간에 예약 시도
      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 = 25INSERT 하려고 하면? → 갭을 막지 않으면 삽입 가능 → 팬텀 리드 발생

그래서 InnoDB는 아예 [20]과 [30] 사이의 갭에도 락을 걸어버립니다.

→ 이것이 Next-Key Lock이고, 그 갭이 갭이다.




락 경합으로 인한 문제 : Blocking, Deadlock

블로킹 : 한 트랜잭션이 락을 보유하고 있을 때, 다른 트랜잭션이 해당 리소스를 기다리며 작업이 지연되는 현상으로 장기화되면 병목현상이 나타난다.

  • 블로킹을 해소하기 위해서는 이전 트랜잭션이 완료(커밋/롤백)되어야한다.
  • 트랜잭션을 짧게 정의해야한다.
  • 설계시 같은 데이터를 갱신하는 트랜잭션이 동시에 수행되지 말아야한다.
  • 트랜잭션 격리수준을 필요이상으로 높게 설정하면 안된다.
  • lock timeout을 이용하여 잠금 해제 시간을 조절하면 된다.



데드락 : 서로가 보유한 락을 서로 기다리면서 영원히 진행되지 못하는 상황으로 A가 B의 자원을 기다리고, B는 A의 지원을 기다리는 구조

  • 접근 순서를 정한다.
  • 락 획득 전 락 존재 여부 체크
  • 트랜잭션을 처리 속도를 최대한 빠르게
  • 데드락 방지를 위해 lock timeout을 걸어 락 잠금해제를 설정한다.
  • 데드락 감지 기능사용


정리

  • 락은 트랜잭션이 처리되는 순서를 보장하여 데이터 무결성을 보장하기 위한 방법이다.
  • 락종류로는 공유락, 배타락이 있고
  • 공유락은 데이터읽기연산용으로 한 트랜잭션이 수행중일떄 공유락을 가진 다른 트랜잭션도 동시에 접근할 수 있다.
  • 반면, 배타락은 데이터를 수정하는 락으로 하나의 트랜잭션을 수행 중일떄 다른 트랜잭션이 접근할 수 없다.
  • 락을 이용할 떄는 트랜잭션이 블로킹/데드락에 걸릴수있다는 한계가 존재하며
  • 여러 방법을 도입하여 한계를 극복해야한다.




결론

  • 락은 데이터 무결성을 지키기 위한 핵심 도구지만, 성능 저하나 데드락과 같은 부작용을 초래할 수 있음
  • 시스템 특성과 동시성 요구 수준에 따라 적절한 락 전략을 선택해야 함
  • 실무에서는 락 종류를 이해하는 것뿐 아니라, 어떤 시점에 어떤 범위로 락을 거는지가 핵심
profile
welcome

0개의 댓글