개발/sql

2 Phase Lock & mysql -> MVCC

방푸린 2024. 11. 6. 15:31
반응형

아래와 같은 로직은 serializable하지 않아 실행 순서에 따라 결과가 다르다(x=100; y=200에서 시작)

유투브: 쉬운코딩

Serializable트랜잭션 격리 수준(iso-level) 중 가장 높은 수준으로, 실행 순서에 상관없이 동일한 결과를 보장합니다.

Serializable은 트랜잭션들이 서로 겹치지 않도록 순차적으로 실행되는 것처럼 보이도록 보장합니다. 즉, 동시에 실행되는 여러 트랜잭션이 서로 간섭하지 않도록 하여, 트랜잭션이 직렬화된 것처럼 처리됩니다.

  1. 결과의 일관성 보장: 여러 트랜잭션이 동시에 실행되더라도, 실행 순서에 관계없이 동일한 결과를 보장합니다. 즉, 트랜잭션 간에 발생할 수 있는 경쟁 조건이나 읽기-쓰기에 의한 문제(예: 더티 리드, 비반영 읽기, 팬텀 리드 등)를 방지합니다.
  2. 트랜잭션 순차성: 데이터베이스는 트랜잭션들이 마치 순차적으로 실행된 것처럼 처리되도록 합니다. 이는 데이터베이스가 내부적으로 잠금 또는 스케줄링을 관리하여 발생할 수 있는 충돌을 막습니다.
  3. 동시성 감소: 여러 트랜잭션이 동시에 실행되면, 그들이 서로 잠금을 요구하거나 기다리는 상태가 발생할 수 있습니다. 이로 인해 성능 저하가 있을 수 있습니다.

 

이를 보장하기 위해선? 


2단계 잠금(2-Phase Locking, 2PL)은 데이터베이스에서 트랜잭션의 일관성과 동시성을 유지하기 위한 잠금 프로토콜입니다. 2단계 잠금 규칙을 따르면 데이터베이스의 ACID 특성을 유지하면서 다중 트랜잭션이 동시에 실행될 때도 무결성을 보장할 수 있습니다.

2단계 잠금(2-Phase Locking)의 원리

: 모든 잠금 작업이 첫 번째 잠금 해제 작업보다 반드시 먼저 이루어지는 것입니다.

  1. 확장 단계(Growing Phase):
    • 트랜잭션은 필요한 모든 잠금을 획득하는 단계입니다.
    • 잠금 해제는 허용되지 않으며 오로지 잠금 획득만 할 수 있습니다.
    • 트랜잭션이 접근하는 데이터에 대해 읽기 잠금 또는 쓰기 잠금을 설정합니다.
  2. 축소 단계(Shrinking Phase):
    • 트랜잭션이 모든 잠금을 해제하는 단계입니다.
    • 이 단계에서는 더 이상 잠금을 획득할 수 없습니다.
    • 트랜잭션이 모든 작업을 완료하고 나면, 잠금을 해제하여 다른 트랜잭션이 접근할 수 있도록 합니다.

2단계 잠금이 일관성을 보장하는 이유

2PL을 따르는 경우, 트랜잭션 간의 교착 상태(Deadlock)나 무결성 문제를 예방할 수 있습니다. 트랜잭션이 모든 잠금을 획득할 때까지 축소를 시작하지 않기 때문에, 중간에 변경되는 데이터를 읽어 일관성이 깨지는 상황을 방지할 수 있습니다.

2단계 잠금의 단점

  • 교착 상태 발생 가능성: 여러 트랜잭션이 서로의 잠금을 기다리다가 교착 상태가 발생할 수 있습니다.
  • 성능 저하: 트랜잭션이 길어질수록 잠금을 오랫동안 유지해야 하므로 다른 트랜잭션의 병렬 실행을 방해할 수 있습니다.

 

종류

  •  2PL
    • 모든 잠금 작업이 첫 번째 잠금 해제 작업보다 반드시 먼저 이루어지는 것
    • 락은 트랜잭션이 끝날 때까지 지속되는 것이 아니라, 쓰기 작업이 완료되는 시점에 해제

  • C2PL
    • 트랜잭션이 시작되기 전에 필요한 모든 잠금을 미리 획득
    • 모든 리소스를 잠근 후, 트랜잭션을 시작하고 잠금 해제는 트랜젝션 처리하자마자

  • S2PL
    • 트랜잭션 시작 시점에 모든 잠금을 미리 걸지는 않음. 대신, 잠금을 필요로 할 때마다 걸고, 획득한 모든 쓰기 잠금을 트랜잭션이 완료될 때까지 유지
    • write lock의 unlock이 커밋 이후

  • SS2PL
    • 각 데이터에 접근할 때 해당 데이터에 대한 잠금을 획득하며, 획득한 모든 잠금은 트랜잭션이 완료될 때까지 유지
    • read/write lock의 unlock이 커밋 이후

 

MySQL의 2PL 사용 방식

MySQL은 InnoDB 스토리지 엔진을 사용할 때, 트랜잭션 격리 수준에 따라 락킹을 관리합니다. InnoDB는 기본적으로 Strict 2-Phase Locking (S2PL)을 사용하며, 이는 일반적인 2PL과 달리 트랜잭션이 종료될 때까지 쓰기 잠금을 유지합니다. 이 방식은 Repeatable ReadSerializable 격리 수준에서 특히 활용됩니다.

  • Repeatable Read 격리 수준: InnoDB의 기본 격리 수준이며, InnoDB는 기본적으로 S2PL을 사용하여 트랜잭션 중에 읽은 데이터가 변경되지 않도록 보장합니다. 추가로 MySQL에서는 멀티버전 동시성 제어(MVCC)를 사용해 읽기 작업에 대한 잠금 경합을 줄입니다.
  • Serializable 격리 수준: 이 수준에서는 MySQL이 트랜잭션 충돌을 방지하기 위해 더 강한 잠금을 사용합니다. 결과적으로 트랜잭션을 직렬화된 순서로 수행하려는 경향이 있으며, SS2PL과 유사한 동작을 제공합니다.

JPA와 데이터베이스 락킹의 관계

JPA에서 특정 락킹을 요청할 때(예: @Lock(LockModeType.PESSIMISTIC_WRITE)), 이 요청은 데이터베이스로 전달되어 MySQL의 2PL 방식에 따라 적용됩니다. 결국, JPA를 통해 락 모드를 설정해도 최종적인 락킹 동작은 데이터베이스의 격리 수준과 락킹 방식에 의해 결정됩니다.

요약

  1. MySQL + JPA 조합에서는 InnoDB의 2PL 구현(S2PL)에 의해 락킹이 수행됩니다.
  2. 트랜잭션 격리 수준 설정에 따라 2PL의 엄격성이나 일관성 수준이 달라집니다.
  3. JPA는 데이터베이스에 락 모드를 요청할 수 있지만, 락킹 방식은 데이터베이스의 구현에 의존합니다.

 

2PL의 문제점

2PL은 데이터에 공유 락(읽기용) 또는 배타적 락(쓰기용)을 걸어서 트랜잭션이 안전하게 처리되도록 합니다. 하지만 몇 가지 문제점이 있습니다:

  1. 데드락 발생 가능성:
    • 여러 트랜잭션이 서로의 락을 기다리는 상태에 빠져서 교착 상태가 발생할 수 있습니다.
  2. 높은 락 대기 시간:
    • 트랜잭션 간의 상호 락으로 인해 읽기와 쓰기가 겹칠 때마다 대기 시간이 길어질 수 있으며, 대기 상태에서 성능 저하가 발생합니다.
  3. 성능 저하:
    • 특히 읽기 작업이 많은 시스템에서 성능이 크게 저하됩니다. 모든 트랜잭션이 락을 걸어야 하므로, 높은 동시성 요구를 충족하기 어렵습니다.

 

MVCC (Multi-Version Concurrency Control)다중 버전 동시성 제어라고 불리는 방식으로, 데이터베이스에서 동시성 제어를 위해 여러 데이터 버전을 관리하여 성능을 향상시키는 기법입니다. 특히 읽기 작업이 많은 환경에서 락을 걸지 않고도 동시성을 보장할 수 있도록 설계되었습니다.

MVCC의 주요 개념

  • 데이터 버전 관리: MVCC에서는 데이터베이스의 각 행(row)에 대해 여러 버전을 저장합니다. 새로운 트랜잭션이 변경을 가할 때마다, 기존 버전을 덮어쓰지 않고 새로운 버전을 생성합니다. 과거의 데이터는 그대로 유지됩니다.
  • 트랜잭션 격리: MVCC는 트랜잭션이 시작될 때의 스냅샷(시점) 기준으로 데이터를 읽도록 하여, 다른 트랜잭션이 데이터를 변경하는 중에도 해당 스냅샷 기준 데이터를 읽게 됩니다. 이를 통해 락 없이도 일관된 읽기 작업을 제공합니다.
  • 삭제 지연: 과거의 버전 데이터는 특정 조건에서 삭제되며, 데이터베이스가 자동으로 불필요한 버전을 제거하는 가비지 컬렉션 작업을 수행합니다.

MVCC의 작동 방식

  1. 트랜잭션 시작 시점의 스냅샷 사용: 트랜잭션이 시작되면 해당 시점의 데이터 스냅샷을 이용하여 데이터 조회를 수행합니다. 이렇게 하면 다른 트랜잭션에서 데이터가 변경되더라도 현재 트랜잭션은 일관성 있는 데이터를 확인할 수 있습니다.
  2. 데이터 버전 관리: 데이터에 대한 수정이 발생할 때 기존 데이터는 그대로 유지하고 새로운 버전을 생성합니다. 예를 들어, A 트랜잭션이 테이블의 특정 행을 수정하면, 원래 데이터를 덮어쓰지 않고 새로운 데이터 버전을 추가하는 방식입니다.
  3. 커밋 후 가시성: 트랜잭션이 완료되면 변경된 데이터 버전이 다른 트랜잭션에서도 보이게 됩니다. 아직 완료되지 않은 트랜잭션에서의 변경은 다른 트랜잭션에 영향을 미치지 않습니다.
  4. 가비지 컬렉션: 시간이 지나면서 더 이상 참조되지 않는 오래된 데이터 버전은 데이터베이스에서 주기적으로 삭제하여 공간을 확보합니다.

MVCC의 장점

  • 락을 사용하지 않고도 일관된 읽기를 보장하므로, 읽기 성능이 우수합니다.
  • 트랜잭션이 많아도 충돌이 적어, 데드락 발생 가능성이 줄어듭니다.
  • 동시 읽기 및 쓰기 작업을 효율적으로 처리하여 높은 동시성을 제공합니다.

MVCC의 단점

  • 모든 데이터의 버전을 유지해야 하므로, 저장 공간이 더 많이 필요할 수 있습니다.
  • 가비지 컬렉션 작업이 필요하여, 오래된 버전을 제거하는 데 추가적인 관리 비용이 발생할 수 있습니다.

적용 사례

MVCC는 PostgreSQL, MySQL의 InnoDB 엔진, Oracle 등 여러 데이터베이스 시스템에서 사용되며, 특히 트랜잭션 격리 수준을 높이면서도 성능을 유지해야 하는 환경에서 널리 활용됩니다.


출처: https://www.youtube.com/watch?v=0PScmeO3Fig

728x90
반응형