참고 자료
MVCC 등장 배경
https://20240228.tistory.com/410
초창기의 RDBMS가 동시성 제어를 위해 사용한 LOCK을 활용하는 2PL 방식은
read - read conflict 케이스를 제외하고는 write - write / write - read / read - write 에서는 트랜잭션들이 서로 대기 상태에 빠지게 되어 처리량이 제한적이었다.
하지만 MVCC(Multi Version Concurrency Control)은 write - write를 제외하고는 블록되지 않는다.
이는 동시 처리량을 2PL 방식과 비교했을 때 많은 성능 차이를 낸다.
MVCC 설명
오늘날의 MVCC는 대부분 write_lock을 활용해서 구현한다.
그래서 예제에서 write(something)이면 기본적으로 write_lock을 획득하고 해당 작업을 하는구나라고 이해하면 좋다.
1. 트랜잭션2 x에 write(x = 50) 작업을 한다.
2. 트랜잭션2번만 아는 공간(개념적인 공간을 의미한다 실제 구현은 DBMS마다 다르다)에 x = 50을 기록한다.
3. 트랜잭션1 read(x)를 하는데 이때 read는 commit된 데이터인 10을 읽는다. MVCC는 commit된 데이터만 읽는다.
4. 트랜잭션2 commit (x에대한 write_lock 반환)
중요한점
MVCC에서 commit 시점에 write_lock 반환은 개발자가 직접 하는 게 아닌 DBMS 구현이 그런 것이다.
또한 commit 시점에 write_lock을 반환하는 이유는 recoverability를 위해서 해당 시점에 write_lock을 반환한다.
4번 트랜잭션2번이 commit 작업까지 마치고 트랜잭션1번이 다시 x에 대한 값을 읽었을 때 어떤 값이 나올까?
해당 결과는 isolation level에 따라 다르다.
Read Committed Level
read 하는 시간을 기준으로 그전에 commit된 데이터를 읽는다.
그래서 50을 읽는다.
MySQL과 PostgreSQL 모두 동일하다.
Repeatable Read Level
트랜잭션 시작 시간을 기준으로 그전에 commit된 데이터를 읽는다.
그래서 10을 읽는다.
MySQL과 PostgreSQL 모두 동일하다.
MVCC 특징 정리
데이터를 읽을 때 특정 시점(트랜잭션 시작 시간/read 하는 시간)을 기준으로 가장 최근에 commit된 데이터를 읽는다
데이터 변화(write) 이력을 관리한다. (추가적인 공간을 사용)
read write는 서로를 block 하지 않는다. (동시 처리량 증가)
MySQL 문서를 읽을 때 "Consistent read" 용어는 특정 시점을 기준으로 commit된 데이터를 읽는다는 의미이다.
Serializable Level
MySQL
- 읽는 데이터 10
- MVCC로 동작하기 보다는 lock으로 동작한다.
PostgreSQL
- 읽는 데이터 10
- SSI(Serializable Snapshot Isolation) 기법이 적용된 MVCC로 동작한다
Read Uncommitted Level
MVCC는 committed된 데이터를 읽기 때문에 이 level에서는 보통 MVCC가 적용되지 않는다
MySQL
MVCC가 적용되는 level은 ReadCommitted / Repeatable Read
PostgreSQL
Read Uncommitted level이 존재하지만 Read committed level 처럼 동작한다
PostgreSQL
commit을 하면 x = 10 y = 50 으로 바뀐다.
commit 이전까지는 트랜잭션1번만 아는 공간에 x = 10, y = 50 실제 데이터는 x = 50, y = 10
트랜잭션2번은 트랜잭션1번이 x에 대한 write_lock을 commit 이후에 반환(해제)하기 때문에 대기(block)상태 였다가
트랜잭션1번이 commit 하고 다시 시작한다.
마찬가지로 바로 트랜잭션2번만 아는 공간에 x = 80 쓰고 트랜잭션2번이 commit하면
x = 80 으로 바꾼다.
최종적으로 x = 80 y = 50 하지만 원하는 결과는 x = 50 y = 50 이다.
즉 트랜잭션1번의 x -= 40 연산이 증발했다.
이런 이상 현상을 LOST UPDATE 라고 한다.
격리 레벨을 repeatable read로 바꿔서 lost update 해결하기
PostgreSQL에서 repeatable read는 같은 데이터에 먼저 update 한 트랜잭션이 commit 되면 나중 트랜잭션은 롤백된다.
먼저 update 한 트랜잭션이 rollback 되면 정상 commit 한다.
이런 특징을 first_updater-win 이라고 한다.
MySQL
MySQL에서는 똑같이 두 트랜잭션을 repeatable read level로 설정하고 진행해도
PostgreSQL에서의 first-updater-win 같은 기능이 없기 때문에 그대로 모두 commit 되고 Lost Update 현상이 일어난다
어떻게 MySQL은 이 문제를 해결할까?
MySQL
MySQL에서 Lost Update를 막으려면 Locking Read 작업이 필요하다.
Locking Read는 쿼리를 작성하는 개발자가 챙겨야 하는 부분이다.
Locking Read X
SELECT balance
FROM account
WHERE id = 'x';
Locking Read O
SELECT balance
FROM account
WHERE id = 'x'
FOR UPDATE;
그림같이 트랜잭션 2번이 먼저 x에 대한 Read Lock을 획득했으면
트랜잭션 1번이 똑같이 x에 대해서 Read Lock을 획득하려고 해도 이미 트랜잭션2번이 락을 획득한 상태여서
대기(블락)상태가 된다.
또한 Locking Read는 가장 최근의 commit된 데이터를 읽는다
즉, Repeatable Read Level은 트랜잭션 시작 시간을 기준으로 그전에 commit된 데이터를 읽지만
Repeatable Read Level 이어도 Locking Read를 통해 Read Committed Level 처럼 데이터를 가장 최근의 commit된 데이터를 읽게 되는 것이다.
정리하면 MySQL은 repeatable read level로 Lost Update 막을 수 없고 Locking Read를 개발자가 챙겨야 막을 수 있다.
MySQL Locking Read 종류
SELECT .. FOR UPDATE;
SELECT .. FOR SHARE;
FOR UPDATE: exclusive lock 즉 write_lock을 획득한다.
FOR SHARE: shared lock 즉 read_lock을 획득한다.
Repeatable Read에서 Write Skew 문제
정상적으로 동작이란? serial schedule을 의미한다.
즉 트랜잭션1 → 트랜잭션2 순서나 트랜잭션2 → 트랜잭션1 순서를 의미한다.
write skew 발생 예제
최종 결과는 x = 20, y = 20 serial-schedule에서 나올 수 있는 결과와 다르다
해당 문제는 MySQL에서는 Locking Read를 통해 막을 수 있다.
PostgreSQL Locking Read 동작
PostgreSQL에서는 FOR UPDATE를 사용하고 같은 데이터에 먼저 update한 트랜잭션이 commit 하면
나중 트랜잭션은 rollback 된다
Serializable Level
MySQL
MySQL에서 serializable level은 모든 읽기에 암묵적으로 for share 가 추가적으로 붙는것과 같이 동작한다.
그래서 MVCC 보단 S2PL에 가깝다.
PostgreSQL
PostgreSQL은 MVCC 중에 특수한 SSI(Serializable Snapshot Isolation)으로 동작한다.
또한 이상 현상을 막기 위해 first_updater-win 기법을 사용한다.
'DataBase > MySQL' 카테고리의 다른 글
[MySQL] Lock을 활용한 concurrency control (2PL) (1) | 2024.11.14 |
---|---|
[MySQL] 트랜잭션 격리 레벨 (5) | 2024.11.14 |
[MySQL] concurrency control 기초2 (recoverability) (1) | 2024.11.13 |
[MySQL] concurrency control 기초1 (schedule, serializability) (1) | 2024.11.13 |
[MySQL] 트랜잭션과 ACID (0) | 2024.11.13 |