ELO
행복하게 살자!
ELO
전체 방문자
오늘
어제
  • 분류 전체보기 (25)
    • 자료구조 (5)
    • 자바 (6)
      • 이펙티브자바 (3)
      • 모던 자바 인 액션 (0)
    • 스프링 (2)
      • 토비의 스프링 (0)
    • 네트워크 (1)
    • 운영체제 (1)
    • 데이터베이스 (2)
      • MongoDB (1)
      • MySQL (0)
    • 우아한테크코스 (4)
      • prolog (4)
      • 회고 (0)
    • 대규모 서비스 (0)
      • ddd (0)
      • msa (0)
    • JPA (0)
    • Infrastructure (3)
      • Docker & Kubernetes (3)

최근 글

hELLO · Designed By 정상우.
ELO

행복하게 살자!

MySQL의 격리수준
데이터베이스

MySQL의 격리수준

2022. 12. 17. 04:30

일반적으로 database를 공부하다 보면 아래와 같은 표의 형식으로 4가지 격리수준을 공부하게 된다.

표준 격리 수준

보통, Repeatable Read 수준에선 Phantom Read가 발생하는 것으로 알려져있다. 하지만 MySQL의 스토리지 엔진으로 InnoDB Engine을 사용하게 되면 Phantom Read가 발생하지 않는다. 

 

이를 이해하기 위해선 MySQL의 InnoDB 스토리지 엔진의 Lock과 MVCC(Multi Version Concurrency Control)이라는 개념을 이해해야 한다.

 

InnoDB Lock

InnoDB는 Lock을 인덱스에 거는 특징이 있다. 인덱스가 없는 테이블이어도 내부적으로 생성한 인덱스를 사용해 잠금을 설정한다.

InnoDB에선 다음과 같이 3가지 종류의 인덱스 락이 있다.

 

1. Record Lock

이는 단일 레코드 하나의 인덱스에 Lock을 설정하는 것이다.

 

2. Gap Lock

이는 Record Lock과는 다르게 특정 레코드와 레코드 사이에 생기는 그 간격을 잠그는 Lock이다. 해당 Lock이 설정된 구역은 무언가 삽입되거나 제거되는 작업을 방지할 수 있다. Gap Lock은 서버 내부적으로 Shared Gap Lock 형태만 존재한다. 그래서 Update, Delete, Select For Update 구문에 의한 Gap Lock(보통은 Exclusive Gap Lock) 이라 하더라도 여러 트랜잭션의 Gap Lock은 서로 충돌하지 않는다. 

 

Gap Lock은 간격을 잠그는 Lock 이다 보니 예기치 못한 데드락이 많이 발생한다. 일반적으로 가장 쉽게 예상할 수 있는 데드락은 트랜잭션 1은 id=1 인 레코드를 변경하고 트랜잭션 2는 id = 2인 레코드를 변경(X-lock을 쥠)하는 상태에서, 서로 상대 트랜잭션이 변경한 레코드에 대해 변경을 요청할 경우 발생한다. 이런 경우는 업데이트 할 때 id를 오름차순으로 정렬해 실행하면 예방할 수 있다. 하지만, 더 복잡한 상황들이 존재한다.

 

3. Next-Key Lock

Record Lock과 Gap Lock을 합쳐 Next-Key Lock이라고 부른다.

 

동등조건 비교 시 사용되는 잠금

  • Primary Key or Unique Index
    • 쿼리 조건이 1건일 경우, Record Lock만 사용 
    • 쿼리 조건이 1건 초과일 경우, Record Lock + Gap Lock 사용
  • Non-Unique Secondary Index
    • 항상 Record Lock + Gap Lock

 

MVCC(Multi Version Concurrency Control)

MySQL의 InnoDB 스토리지 엔진은 비교적 높은 격리수준에서도 lock 없이 일관된 읽기(Consistent Read)를 보장한다. MySQL에선 조회할 때, 다른 트랜잭션에서 데이터를 변경해도 undo log에 이를 snapshot 으로 기록해 동일한 결과를 반환한다. 이를, MVCC(Multi Version Concurrency Control)이라고 한다. 

 

1. Read Uncommitted

- 가장 낮은 격리수준으로 MVCC를 사용하지 않는다. 따라서 기존의 SQL 표준과 같은 문제들이 발생한다.

 

2. Read Committed

- MVCC가 적용된다. 레코드마다 snapshot이 최신화되어 가장 최신 버젼의 레코드를 읽음으로 Non Locking Consistent Read를 보장한다.

 

3. Repeatable Read

- Read Comitted 단계와 비슷하게 MVCC를 통해 Consistent Read를 보장한다. 다른 점은, Read Comitted 단계는 undo log 영역에서 항상 최신의 레코드를 읽어온다. 하지만, Repeatable Read에선 undo log 영역의 자신의 트랜잭션 id 보다 낮은 버젼 중 가장 최신의 것을 읽음으로 Unrepeatable Read 문제를 방지한다. 또한, Phantom Read의 경우도 snapshot을 읽어옴으로써 방지할 수 있다.

 

 

4. Serializable

- Serializable 격리 수준은 MVCC에 더불어 조회를 항상 select for share로 실행한다. 모든 조회에 공유락을 걸기 때문에 성능이 매우 떨어져 거의 사용하지 않는다.

 

References

- Real My SQL 8.0

- https://medium.com/daangn/mysql-gap-lock-%EB%8B%A4%EC%8B%9C%EB%B3%B4%EA%B8%B0-7f47ea3f68bc

 

MySQL Gap Lock 다시보기

우리가 일반적으로 알고 있는 데이터베이스 서버의 잠금(Lock)은 레코드 자체에 대한 잠금(Record Lock)이에요. 어떤 트랜잭션에서 레코드를 변경하기 위해서는 그 레코드를 잠그고, 그 동안은 다른

medium.com

- https://devlog-wjdrbs96.tistory.com/334

 

[MySQL] 트랜잭션 고립 레벨이란 무엇일까?

MySQL에서의 트랜잭션 MySQL의 동시성에 영향을 미치는 잠금(Lock), 트랜잭션, 그리고 트랜잭션의 격리 수준을 살펴보겠습니다. 트랜잭션은 작업의 완전성을 보장해 주는 것입니다. 잠금: 트랜잭션

devlog-wjdrbs96.tistory.com

 

    ELO
    ELO

    티스토리툴바