kimyu0218
  • [DB/쉬운코드] MVCC
    2024년 05월 13일 17시 10분 19초에 업로드 된 글입니다.
    작성자: @kimyu0218

    MVCC; multiversion concurrency control

    lock 기반의 동시성 제어는 같은 데이터를 read 하는 경우는 허용하지만, 그 외의 경우는 모두 허용하지 않는다. 이는 동시에 실행될 수 있는 트랜잭션을 제한하여 성능 저하를 유발한다. 반면, MVCC는 같은 자원에 대해 write 하는 경우만 허용하지 않는다.
     
    MVCC는 특정 시점을 기준으로 가장 최근에 commit된 데이터를 읽는다. 특정 시점은 isolation level에 따라 달라진다. MVCC는 데이터의 변화 이력을 관리해야 하므로 추가적인 저장 공간이 필요하다.
     

    isolation level

    🚨 MVCC는 commit된 데이터를 읽으므로 read uncommitted level은 MVCC가 적용되지 않는다.

    read committedread 하는 시간을 기준으로 그 전에 commit 된 데이터를 읽는다. read와 write가 서로를 block하지 않기 때문에 변경된 값인 `50`을 읽게 된다.
    반면, repeatable read트랜잭션 시작 시간을 기준으로 그 전에 commit된 데이터를 읽는다. 트랜잭션 시작 시의 `x`값이`10`이였으므로 `10`을 읽는다. 

     read uncommittedread committedrepeatable read
    MVCCXOO
    특정 시점-read 시점 (= 최근 snapshot)트랜잭션 시작 시점 (= 시작 snapshot)
    기본 동작--MySQL

     

    PostgreSQL의 lost update 해결 방법

    read committed에서 lost update 이상 현상이 나타날 수 있다.

    1. `x`와 `y`의 초기 상태는 `x=50`, `y=10`이다.
    2. `tx1`이 `x`를 write 하기 위해 write lock을 획득한다.
    3. `x`를 write 하려는 `tx2`는 write lock에 의해 block된다.
    4. `tx1`이 commit되어 `x=10`, `y=50`이 된다.
    5. block된 `tx2`가 다시 실행되어 `x`가 `80`이 된다.
    6. serializable한 `x=40`, `y=50`과 다른 값이 저장된다.

    위처럼 `tx1`의 update 정보가 사라지는 현상을 lost update라고 한다.
    이는 `tx2`의 isolation level을 repeatable read로 변경하여 해결할 수 있다. PostgreSQL의 repeatable read는 같은 데이터에 대해 먼저 write한 트랜잭션이 commit 되면 나중에 write한 트랜잭션은 rollback된다. (first-updater-win)

    🚨 트랜잭션은 서로 다른 isolation level을 가질 수 있다. 

     

    MySQL의 lost update 해결 방법

    MySQL은 repeatable read만으로 lost update를 해결할 수 없다. 따라서 locking read가 필요하다.

    SELECT x FROM [table_name] FOR UPDATE; # read지만 write lock 획득
    1. `tx2`가 `x`를 read할 때 lock을 획득한다.
    2. `tx1`가 `x`를 read 하려는 시도는 lock을 획득할 수 없기 때문에 block 된다.
    3. `tx2`가 `x`값을 `80`으로 update한다.
    4. `tx1`이 block된 read 작업을 수행한다.
    `FOR UPDATE` `FOR SHARE`
    exclusive lock (= write-lock)shared lock (= read-lock)

     

    write skew 해결 방법

    데이터베이스의 초기 상태는 `x=10`, `y=10`이다. 나올 수 있는 정상적인 결과는 ① `x=20`, `y=30`이거나 ② `x=30`, `y=20`이다.

    하지만 repeatable read의 상황에서 위와 같은 write skew 현상이 발생할 수 있다. write skew는 두 개 이상의 레코드를 동시에 읽고 업데이트할 때 발생하는 이상 현상이다.

    📝 lost update는 동일한 레코드에 대한 동시 write 하는 작업에서, write skew는 여러 레코드에 대한 작업에서 발생하는 이상 현상이다.

     
    repeatable read에서 write skew를 해결하기 위해서는 locking read를 적용하면 된다.

    locking read 적용 후
    🚨 repeatable read level에서 locking read는 DB에 따라 동작 방식이 다르다. PostgreSQL의 repeatable read는 나중 트랜잭션을 무조건 rollback 하기 때문에 `tx1`만 성공하여 `x=20`, `y=10`이 된다.

    참고자료

    댓글