데이터베이스

동시성 제어와 Snapshot Isolation

wwns 2023. 9. 11. 20:19
반응형

이전 글에서 트랜잭션의 격리성과 격리 수준에 따라 발생할 수 있는 문제점을 알아보았다

 

그렇다면, 정말 데이터의 일관성이 중요해 절대 일관성이 깨지면 안 되는 중요 정보에 대해 높은 동시성을 제공 데이터베이스의 일관성을 깨지 않고 데이터를 제공해 줄 수 있는 방법은 높은 격리 수준을 유지하는 것뿐인가?

 

Serializable Read을 보면 락을 걸어 테이블의 접근을 막는다고 했다

락을 걸어 데이터 일관성이 깨지지 않는 높은 격리성을 제공할 수 있지만 이는 데드락을 쉽게 초래할 수 있다

 

그렇다면 트랜잭션 격리 수준에 이어서  Lock에 대한 개념을 좀 더 알아보고 DBMS에서 제공되는 동시성 제어 방법에 대해 알아보도록 하자


동시성 제어

  • 다중 사용자에 의해 작용하는 다중 트랜잭션에서 Database를 보호하는 것
  • 높은 동시성을 제공하면 일관성이 낮아질 수 있음

DBMS에서는 동시성 제어를 하기 위해 Lock 기능과 트랜잭션 격리 수준 설정 기능(`SET TRANSACTION ISOLATION LEVEL`)을 제공한다


Lock 기능

 

  • 낙관적 동시성 제어(Optimistic Concurrency Control), 낙관적 락
    • 사용자들이 같은 데이터 동시에 수정을 하지 않을 것이라 가정
    • 데이터를 읽는 시점에 Lock을 걸지 않는 대신 수정 시점에 값이 변경되었는지 검사하는 방식
    • 예시 
      • name: john, version: 1인 상태의 데이터를 트랜잭션1, 2가 읽었고, 트랜잭션2에서 name을 tom으로 바꾸고 commit
      • 트랜잭션1도 name을 바꾸고 commit 하려 했으나 version이 2로 최신화되어 있어 abort
  • 비관적 동시성 제어(Pessimistic Concurrencty Control), 비관적 락
    • 사용자들이 같은 데이터를 동시에 수정할 것이라 가정
    • 데이터를 읽는 시점에 Lock을 걸고 트랜잭션이 완료될 때까지 유지한다
    • SELECT 시점에 Lock을 거는 비관적 동시성 제어는 성능을 현저히 떨어뜨릴 수 있으므로 wait, no wait 옵션을 같이 사용해야 한다
    • 작동 방식 
      • 데이터베이스에서 Shared Lock(공유 락, 읽기 잠금)이나Exclusive Lock(배타 락, 쓰기 잠금)을 건다
      • Shared Lock의 경우, 다른 트랜잭션에서 읽기만 가능 또한 Exclusive lock 적용이 불가능 (읽는 동안 변경을 막음)
      • Exclusive lock의 경우. 다른 트랜잭션에서 읽기, 쓰기가 둘 다불가능 또한 Shared, Exclusive Lock 적용이 추가적으로 불가능 (쓰는 동안 읽거나, 다른 쓰기가 오는 것을 막음)
    • 예시
      • 1번 트랜잭션이 공유락을 가져간 경우
        • 2번 트랜잭션이 데이터를 읽는 경우는 데이터가 일관되므로, 2번 트랜잭션이 또 다른 공유락을 가져가면서 동시에 처리함
        • 2번 트랜잭션이 데이터를 쓰는 경우는 1번 트랜잭션과 데이터가 달라질 수 있므로 1번 트랜잭션 종료까지 기다려야 함
      • 1번 트랜잭션이 배타락을 가져간 경우
        • 2번 트랜잭션이 데이터를 읽는 경우, 1번 트랜잭션이 데이터를 변경할 수 있으므로 기다림
        • 2번 트랜잭션이 데이터를 쓰는 경우에도, 1번 트랜잭션이 데이터를 변경할 수 있으므로 기다림

두 가지 락 방식을 제공하지만 낙관적 락은 실제로 락을 거는 것이 아니라 버전을 통해 동시성을 관리한다 -> 충돌이 자주 발생하지 않는 상황에 적합(낙관적) -> 이후 Snapshot Isolation과 유사한 메커니즘이라고 생각함

 

비관적 락의 경우 락을 사용하여 데이터의 일관성이 깨지지 않는다는 장점을 가지지만 대기하는 상황이 발생한다는 것은 데드락의 원인이 됨

 

데이터 무결성이 깨진다는 것은 데이터베이스의 신뢰를 잃는다는 의미이기도 하다

위에서 살펴본 락 기능은 높은 동시성을 제공해야 하는 것이 아니라면 충분히 고려해 볼 수 있다고 생각했다

하지만 우리가 원하는 높은 동시성에서 락을 사용하지 않으면서 데이터 일관성이 깨질 위험이 적은 방식은 없을까?

Snapshot Isolation에 대해 알아보자


Snapshot Isolation

 

스냅샷이라는 것은 특정 시점에서의 형상을 말한다 

트랜잭션의 시작하는 시점에 스냅샷을 찍어두면 -> 하나의 트랜잭션에서 값을 변경해도 데이터베이스에 바로 반영되어 있는 것이 아니기 때문에 원본 데이터를 읽을 수 있음 

 

예시를 보자

 

데이터베이스에 데이터 x: 50, y: 50를 조회하는 두 트랜잭션1, 2가 발생

  • 트랜잭션1이 읽는 시점에 스냅샷을 남긴다 -> 데이터 x: 50, y: 50
  • 트랜잭션1이 x의 값을 10으로 변경 -> DB가 아닌 스냅샷에 반영
  • 트랜잭션2가 x, y를 읽고 스냅샷을 남긴다 -> 데이터 x: 50, y: 50
  • 트랜잭션2가 y의 값을 100으로 변경하고 commit 한다
  • 트랜잭션1이 y의 값을 읽는다 -> y: 50(스냅샷의 데이터를 읽음)
  • 트랜잭션1이 y의 값을 80으로 변경하고 commit 한다 -> abort

Snapshot Isolation이라는 것은 같은 데이터에 대한 commit이 발생하면 먼저 반영된 commit만 인정하고 abort시킴

Snapsho Isolation은 MVCC의 한 종류로써 MVCC는 각각의 트랜잭션 시점을 각 버전별로 스냅샷을 남겨놓고 동시성을 제어 하는 방식

 

위의 내용을 봤을 때 공유 락과 비슷하다고 생각했다

 

공유 락은 읽기에 대한 요청은 허가하지만, 쓰기에 대한 요청은 공유락이 해제될 때까지 기다렸다가 수행하도록 한다

스냅샷은 쓰기에 대한 요청도 허가하지만 어떤 트랜잭션이 먼저 commit 되느냐에 따라 다른 트랜잭션은 abort 된다

 

그렇다면 어떤 방식이 더 좋은 방식일까?

 

내 생각은 다음과 같다

  • 스냅샷 Isolation의 경우 스냅샷을 관리해야 한다는 오버헤드가 존재하지만 을 사용하지 않기 때문에 데드락 위험을 방지할 수 있다
  • 여러 쓰기 트랜잭션의 경우에는 롤백되는 트랜잭션이 많이 존재할 수 있으므로 쓰기에서 어느 정도 희생이 필요하다
  • 공유 락의 경우 동시에 쓰기 요청을 보내도 데이터 일관성을 희생하지 않고 모두 반영할 수 있다
  • 락을 사용하는 만큼 동시성은 떨어지며 데드락 위험이 존재한다

따라서

  • 데이터 일관성과 무결성이 중요하다면 공유 락과 같은 락 기반의 방식을 고려
  • 높은 동시성이 필요하며 데이터 일관성 비용을 지불할 수 있다면 Snapshot Isolation과 같은 방식을 고려

추가로 Snapshot Isolation을 사용하면 앞서 살펴보았던 Serializable의 격리 수준을 제공할 수 있다고 한다

 

Snapshot Isolation을 공부했으니 다음은 MySQL에서의 MVCC 방식을 알아보자!


Reference

 

https://learn.microsoft.com/ko-kr/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server

 

SQL Server의 스냅샷 격리 - ADO.NET

SQL Server의 스냅샷 격리 및 행 버전 관리에 대한 개요를 읽고 격리 수준으로 동시성을 관리하는 방법을 알아봅니다.

learn.microsoft.com

https://mangkyu.tistory.com/288

 

[MySQL] MVCC(다중 버전 동시성 제어)와 데이터베이스가 트랜잭션을 지원하는 방법과 동작 과정

이번에는 데이터베이스가 트랜잭션을 지원하는 방법과 동작 과정에 대해 살펴보도록 하겠습니다. 아래의 내용은 RealMySQL과 MySQL 공식 문서 등을 참고하여 작성한 내용입니다. 1. MVCC(다중 버전 동

mangkyu.tistory.com

 

반응형