MySQL 트랜잭션의 격리 수준

1. 트랜잭션 격리 수준

트랜잭션의 격리 수준은 여러 트랜잭션이 동시에 처리될 때 다른 트랜잭션이 수정하거나 검색한 데이터를 특정 트랜잭션이 볼 수 있도록 허용할지 여부를 결정합니다.

격리 수준은 대략 네 가지 범주로 나뉩니다.

  • 필독
  • 의무 사항을 읽으십시오
  • 반복 읽기
  • SERIALIZABLE (직렬화 가능)

각 트랜잭션 간의 데이터 격리 수준은 네 가지 격리 수준에서 뒤로 물러나면서 증가하고 동시 처리 능력은 감소하지만 불일치 문제의 가능성은 줄어듭니다. 격리 수준이 높을수록 MySQL 서버의 처리 능력이 낮다고 생각할 수 있습니다.

2. 격리 수준에 따라 가능한 문제

격리 수준에 따라 발생하거나 발생하지 않을 수 있는 세 가지 불일치 문제가 있습니다.

더러운 읽기 반복 불가능한 읽기 팬텀 읽기
필독 세대 세대 세대
의무 사항을 읽으십시오 존재하지 않는다 세대 세대
반복 읽기 존재하지 않는다 존재하지 않는다 세대
(InnoDB 없음)
직렬화 가능 존재하지 않는다 존재하지 않는다 존재하지 않는다

2.1. 더러운 독서

한 트랜잭션에서 처리 중인 작업이 완료되지 않았는데도 다른 트랜잭션에서 가져올 수 있는 현상입니다. 더티 읽기 현상은 데이터가 나타나고 사라지는 원인이 되므로 개발자와 사용자에게 상당히 혼란스러울 수 있습니다.

2.2. 반복 불가능한 읽기

트랜잭션 내에서 동일한 SELECT 쿼리를 실행하면 읽은 데이터(결과)가 달라집니다. 반복되지 않는 읽기는 일반적인 웹 프로그램에서는 큰 문제가 되지 않을 수 있지만 금융 처리와 관련된 트랜잭션에서 동일한 데이터를 여러 번 읽고 수정하는 경우에는 문제가 될 수 있습니다.

2.3. 팬텀 읽기

다른 트랜잭션에서 수행한 변경 작업으로 인해 레코드가 보이지 않는 현상입니다. InnoDB에서는 고유한 특성으로 인해 REPEATABLE READ 격리 수준에서도 PHANTOM READ가 발생하지 않습니다.

3. 열람금지

READ UNCOMMITTED 격리 수준에서 COMMIT이든 ROLLBACK이든 모든 트랜잭션의 변경 사항을 다른 트랜잭션에서 볼 수 있습니다.

자유롭게 읽기

위에서 Transaction 1은 ID가 2이고 이름이 “짱가”인 사용자를 삽입합니다. 트랜잭션 1이 변경 사항을 커밋하기 전에 트랜잭션 2는 ID=2인 사용자를 검색하지만 트랜잭션 2는 커밋되지 않은 상태에서도 트랜잭션 1이 삽입한 사용자 정보를 검색할 수 있습니다.

그런데 문제는 Transaction 1이 알 수 없는 문제로 처리 중 INSERT 내용을 롤백하더라도 Transaction 2는 여전히 “짱가”가 일반 사용자라고 생각하고 처리를 계속한다는 점이다. 따라서 더티 읽기가 허용되는 격리 수준은 READ UNCOMMITTED입니다.

더티 읽기를 유발하는 READ UNCOMMITTED는 RDBMS 표준에서 트랜잭션 격리 수준으로 인식되지 않는 일관성 문제가 많은 격리 수준입니다.

4. 의무 사항 읽기

READ COMMITTED 격리 수준에서는 한 트랜잭션이 데이터를 수정하더라도 COMMITTED된 데이터만 다른 트랜잭션에서 검색할 수 있습니다.

읽기 의무

위에서 트랜잭션 1은 ID가 2인 사용자의 이름을 “짱가”에서 “힌디어”로 변경했습니다. 이때 새 값 “Hindi”가 테이블에 즉시 입력되고 이전 값 “짱가”가 실행 취소 영역에 저장됩니다. Transaction 1이 커밋되기 전에 Transaction 2가 ID = 2인 사용자를 선택하면 검색 결과의 Name 열 값은 “Albino”가 아니라 “짱가”입니다.

여기서 트랜잭션 2의 SELECT 쿼리 결과는 테이블에서 오는 것이 아니라 실행 취소 영역에 저장된 데이터 세트에서 나옵니다.

5. 반복 읽기

REPEATABLE READ는 MySQL의 InnoDB 스토리지 엔진에서 사용하는 기본 격리 수준입니다. 트랜잭션 ROLLBACK 가능성에 대비하여 InnoDB 스토리지 엔진은 레코드가 undo 영역에서 수정되기 전에 백업하고 실제 레코드 값을 수정하는데 이러한 수정 방법을 MVCC(Multi Version Concurrency Control)라고 합니다.

REPEATABLE READ 격리 수준은 MVCC의 실행 취소 영역에 저장된 이전 데이터를 사용하여 동일한 트랜잭션 내에서 동일한 결과를 볼 수 있도록 합니다. 모든 InnoDB 트랜잭션에는 고유한 트랜잭션 번호(증가하는 값)가 있으며 실행 취소 영역에 백업된 모든 레코드에는 변경을 일으킨 트랜잭션 번호가 포함됩니다.

REPEATABLE READ 격리 수준에서 트랜잭션 번호가 현재 트랜잭션 중 가장 오래된 트랜잭션 번호보다 이전인 Undo 영역의 데이터는 MVCC를 보장하기 위해 삭제할 수 없습니다. 특정 트랜잭션 번호 범위에 저장된 Undo 데이터는 유지되어야 합니다.

반복 읽기

이 시나리오가 실행되기 전에 트랜잭션 번호 6 테이블이 삽입되었다고 위에서 가정합니다. 트랜잭션 12는 사용자 이름을 “albino”로 변경하고 커밋했습니다. 그러나 트랜잭션 10은 트랜잭션 12의 변경 전후에 id=2인 사용자를 한 번 선택했고 결과는 항상 “짱가”를 리턴한다.

트랜잭션 10에서 실행되는 모든 SELECT 쿼리는 트랜잭션 번호가 10 미만인 트랜잭션 번호(자체 트랜잭션 번호)의 변경 사항만 볼 수 있습니다.

6. 직렬화 가능

SERIALIZABLE 격리 수준에서 지정된 트랜잭션이 읽고 쓴 레코드는 다른 트랜잭션에서 액세스할 수 없습니다. 데이터 일관성은 가장 높지만 성능은 가장 낮습니다. 단순 SELECT 쿼리를 실행하더라도 다른 트랜잭션이 데이터에 접근하지 못하도록 잠금을 획득한다.