- Published on
트랜잭션과 MVCC
- 글쓴이
📌 트랜잭션과 MVCC
트랜잭션과 MVCC에 대해서 애매하게 넘어갔던 부분을 정리하고, 후루룩 쉽게 설명할 수 있는 사람이 되기 위해 정리한다.
트랜잭션?
데이터베이스에서의 트랜잭션은 작업의 최소 단위다. 트랜잭션은 여러 작업을 하나의 작업처럼 처리하기도 한다. 트랜잭션이 성공하면 모든 작업이 반영(commit)되고, 실패하면 이전 상태로 돌아간다.(rollback)
ACID? 액시드?
이를 위해 ACID라는 네 가지 특징을 충족해야 한다. 무지성으로 DB를 믿는 것이 아니라, 개발자도 안전한 코드를 작성하기 위해 노력해야 한다.
- 원자성(Atomicity): 트랜잭션의 모든 작업이 전부 성공하거나 전부 실패해야 한다. (DBMS가 보장)
- 일관성(Consistency): 트랜잭션 전후에 데이터의 무결성이 유지되어야 한다. (개발자가 보장)
- 격리성(Isolation): 각 트랜잭션은 다른 트랜잭션의 중간 상태를 보지 않고 독립적으로 실행된다. (개발자가 보장)
- 영속성(Durability): 트랜잭션이 성공하면, 그 결과는 시스템 오류에도 영구적으로 반영되어야 한다. (DBMS가 보장)
**그러나 **ACID 특성이 모든 트랜잭션 격리 수준에서 보장되는 것은 아니다. Serializable 수준에서만 모든 특성이 완벽히 지켜지고, 낮은 수준에서는 성능과 동시성을 위해 일부 특성을 양보해야 한다.
예시 Read Committed : Dirty Read X , **Non-Repeatable Read(NRR)**와 Phantom Read(PR) O 개발자는 NRR, PR 같은 문제를 고려해 코드를 작성해야 한다.
트랜잭션 격리 수준과 이상 현상(Anomaly)
요약. 외우자.
- Read Uncommitted: Dirty Read, NRR, PR 가능
- Read Committed: Dirty Read 방지, NRR, PR 가능
- Repeatable Read: Dirty Read, NRR 방지, PR 가능
- Serializable: 모든 이상 현상 방지
Serializable이 가장 높은 격리 수준, 성능 저하와 데드락 위험이 높다. 반면, Read Committed는 성능은 뛰어나지만 일관성 보장이 약해 개발자가 추가로 처리해야 한다.
MVCC와 동시성 제어
**다중 버전 동시성 제어(MVCC)**는 동시성 문제를 보다 우아하게 해결하기 위한 기술이다. MVCC는 데이터의 여러 버전을 유지해 트랜잭션 간 충돌을 피한다. 트랜잭션이 데이터를 읽을 때, 현재 시점의 데이터가 아니라 트랜잭션 시작 시점의 스냅샷을 참조한다. 다른 트랜잭션이 데이터를 변경하더라도 영향을 받지 않는다.
Non-Repeatable Read/ Phantom Read 문제를 방지하고, 성능 저하 없이 데이터 일관성을 보장한다.
MySQL과 MariaDB는 Repeatable Read 격리 수준에서 MVCC를 사용해 트랜잭션 내에서 일관된 데이터를 제공한다.
동시성 제어와 Lock
그러면 개발자는구체적으로 어떻게 해야할까? 동시성 문제를 해결하는 방법 중 하나가 **낙관적 락(Optimistic Lock)**과 **비관적 락(Pessimistic Lock)**이다.
- 낙관적 락: 트랜잭션이 데이터를 읽을 때는 락을 걸지 않지만, 변경을 시도할 때 락을 거는 방식이다. 충돌 가능성이 낮은 상황에서 사용한다.
- 비관적 락: 데이터를 읽을 때부터 락을 걸어 다른 트랜잭션이 데이터를 수정하지 못하게 한다. 충돌 가능성이 높은 상황에서 적합하다.
개발자는 상황에 맞게 두 가지 락을 선택해 사용해야 하며, 성능을 이유로 락을 걸지 않는 것은 위험할 수 있다. 특히, 습관적으로 쓰는 ***MSSQL의 WITH (NOLOCK)**와 같은 기능을 성능 최적화를 위해 남용하면, Dirty Read 문제가 발생할 수 있다.