Lock ( 데이터베이스 락 ) 이란?

세션1이 트렌젝션을 시작하고 데이터를 수정하는 동안 아직 커밋을 수행하지 않았는데, 세션2에서 동시에 같은 데이터를 수정하게 되면 여러가지 문제가 발생한다.

이런문제를 방지하기 위해, 세션이 트렌젝션을 시작하고 데이터를 수정하는 동안에 커밋이나 롤백 전까지 다른세션에서 해당 데이터를 수정할 수 없게 막아야 한다. 이런 문제를 해결하기 위해 DB에서는 LOCK 락 이라는 개념을 제공한다.

  • 락은 언제 사용하는가? : 트렌젝션 종료 시점까지 해당 데이터를 다른 곳에서 변경하지 못하도록 강제로 막아야 할 때 사용한다.
  • 일반적인 조회는 락을 사용하지 않는다.
  • 락 타임아웃(다른 세션이 데이터를 점유하고 있을때 대기시간이 초과된 경우)에는 익셉션이 발생하게 된다.

1. 동시성 제어 정의

동시성 제어(Concurrency Control)란, 여러 사용자나 프로세스가 데이터를 공유할 때 발생할 수 있는 문제를 해결하기 위해, 데이터베이스 시스템이 트랜잭션을 순서대로 실행하는 것이 아니라, 트랜잭션이 동시에 실행될 수 있도록 허용하면서도 데이터의 일관성과 무결성을 유지할 수 있도록 하는 기술이며, 데이터베이스 시스템에서 매우 중요한 개념 중 하나이다.

2. 동시성 제어 목적

  • 여러 사용자가 DB에 접근하더라도 데이터의 일관성을 보장하고 데이터의 무결성을 유지
  • 위를 만족하며 데이터베이스 시스템의 성능과 효율성을 유지하는 것
  1. 분실된 갱신(Lost Update)
    • 두개의 트랜잭션이 같은 데이터를 갱신하는 작업을 진행하게 되면서 하나의 작업이 진행되지 않는 경우
  2. 모순성(Inconsistency)
    • 두개의 트랜잭션이 같은 데이터를 동시에 갱신하게되어 사용자가 원하는 결과와 일치하지 않은 상태가 되는 경우
  3. 연쇄복귀(Cascading Rollback)
    • 두개의 트랜잭션이 같은 데이터를 갱신하는 작업을 진행하는 과정에서 하나의 트랜잭션이 실패하면 원자성에 의해 두 트랜잭션 모두 복귀하는 경우
  4. 비완료 의존성(Uncommitted Dependency)
    • 한개의 트랜잭션이 실패하였을때, 이 트랜재션이 회복하기전에 다른 트랜잭션이 실패한 수행 결과를 참조하는 경우

3. 동시성 제어 방법

Locking이란?

  • Locking은 공유 자원에 대한 동시 액세스를 제어하는 전통적인 방법이다.
  • 단일 스레드 또는 단일 프로세스에서만 동작하며, 한 번에 하나의 스레드만 공유 자원에 액세스할 수 있다.
  • 신뢰성과 안전성이 높으며, 어떤 수준의 locking을 적용하느냐에 따라 교착 상태나 경쟁 조건과 같은 문제를 방지할 수 있다.
  • 하지만 동시성 처리 속도가 저하될 수 있고, 대기 시간이 발생할 수 있다.
  • 기본적으로 lock 연산과 unlock 연산을 사용한다.

Locking의 종류

  • 공유 잠금 (shared lock/s-lock): 데이터를 읽을 때 사용하는 락

    • 공유잠금을 설정한 트랜잭션은 데이터 항목에 대해 읽기 연산(read)만 가능하다.
      • T1에서 x에 대해 S-lock을 설정했다면, T1은 read(x) 연산만 가능하다.
    • 하나의 데이터 항목에 대해 여러 개의 공유잠금이(S-lock) 가능하다.
      • T1에서 x에 대해 S-lock을 설정한 경우, 동시에 T2에서도 x에 대해 S-lock을 설정할 수 있다.
    • 다른 트랜잭션도 읽기 연산(read) 만을 실행할 수 있다.
      • T1에서 x에 대해 S-lock을 설정했다면, T2에서도 T1이 S-lock(x)을 실행하는 동안 read(x) 연산만 가능하다.
  • 배타 잠금 (exclusive lock/x-lock): 데이터를 변경할 때 사용하는 락

    • 배타잠금을 설정한 트랜잭션은 데이터 항목에 대해서 읽기 연산(read)과 쓰기 연산(write) 모두 가능하다.
      • T1에서 x에 대해 S-lock을 설정했다면, T1은 read(x) 연산과 write(x) 연산 모두 가능하다.
    • 하나의 데이터 항목에 대해서는 하나의 배타잠금(X-lock)만 가능하다.
    • 동시에 여러 개의 배타잠금은 불가능하다.
      • T1에서 x에 대해 X-lock을 설정했다면, T1에서 unlock(x)를 하기 전까지 T2에서 x에 대해 X-lock을 설정할 수 없다.
    • 다른 트랜잭션은 읽기 연산(read)와 쓰기 연산(write) 모두 불가능하다.
      • T1에서 x에 대해 X-lock을 설정했다면, T2에서는 T1에서 unlock(x)를 하기 전까지 read(x), write(x) 연산이 모두 불가능하다.
  • 추가) 교착상태 (deadlock)

    • 모든 transaction이 대기 상태에 들어가 아무런 진행이 일어나지 않는 상태를 교착상태라고 한다. 교착상태에 빠지면 외부에서 강제로 트랜잭션을 중단하거나 잠금을 해제하지 않는 이상 무한정 대기 상태로 남게 된다.

Locking을 활용한 동시성 제어 기법

  • 낙관적 락 (optimistic lock)

    • 충돌이 발생할 가능성이 낮은 경우 사용되는 동시성 제어 기법이다.
    • 충돌이 발생하면 재시도 또는 병합을 통해 충돌을 해결한다.
    • 실제로 lock을 사용하지 않고 version을 이용함으로서 정합성을 맞추는 방법이다. 데이터를 읽을 때 lock을 사용하지 않고, 업데이트 시 내가 읽은 version이 맞는지 충돌 여부를 확인하여 처리한다.
    • 즉, 자원에 lock을 직접 걸어서 선점하지 않고, 동시성 문제가 실제로 발생하면 그때가서 처리하는 방식이다.
  • 비관적 락 (pessimistic lock)

    • 충돌이 발생할 가능성이 높은 경우 사용되는 동시성 제어 기법이다.
    • 데이터를 읽거나 수정하기 전에 lock을 획득하여 다른 사용자의 액세스를 차단하고, lock을 가진 스레드만 접근하도록 제어한다.
    • 데이터에 대한 배타적인 액세스 권한을 보장하여 충돌을 방지한다.
    • 실제로 데이터에 lock을 걸어서 정합성을 맞추는 방법으로, 자원 요청에 따른 동시성 문제가 발생할 것이라고 예상하고 lock을 걸어버리는 방법이다.
    • 즉, 트랜젝션이 시작할 때 s-lock이나 x-lock을 실제로 걸고 시작한다.
  • 낙관적 락 vs. 비관적 락 Pasted image 20250228150552.png

  • 분산락 (distributed lock)

    • 여러 컴퓨터 또는 프로세스 간에 공유된 자원에 대한 동시 액세스를 제어하기 위해 사용된다.
    • 분산 시스템에서 동시성 문제를 해결하기 위해 사용되며, 분산된 서버 또는 클러스터 간의 상호작용이 필요하다.
    • 주로 데이터베이스나 메시지 큐 등의 분산 시스템에서 사용된다.
    • 대표적인 분산락 기법으로는 ZooKeeper, Redis 등이 있다.
    • Redis는 RedLock이라는 알고리즘을 제안하며 3가지 특성을 보장해야한다고 한다.
      • 오직 한 순간에 하나의 작업자만이 락(lock) 을 걸 수 있다.
      • 락 이후, 어떠한 문제로 인해 락을 풀지 못하고, 종료된 경우라도 다른 작업자가 락을 획득할 수 있어야합니다.
      • Redis 노드가 작동하는한, 모든 작업자는 락을 걸고 해체할 수 있어야합니다.
    • 분산 락을 구현하기 위해 lock에 대한 정보를 Redis에 저장하고 있어야한다. 그리고 분산환경에서 여러대의 서버들은 공통된 Redis를 바라보며, 자신이 공유 자원에 접근할 수 있는지 확인한다.
  • 스핀락 (spin lock)

    • 자원에 대한 접근이 필요할 때 무한루프를 돌면서 반복적으로 확인하며, 다른 스레드가 lock을 해제할 때까지 대기한다.
    • 경쟁 상태 (2개 이상의 프로세스가 공유 자원을 동시에 읽거나 쓰는 상황)가 짧고 자원 점유 시간이 길지 않은 경우에 효과적이다.
    • 주로 멀티코어 시스템에서 사용되며, 락 획득을 위해 CPU를 계속 사용하므로 서버에 많은 부하를 주어 주의해야 한다.