안녕세계
[MySQL] Transaction Isolation 본문
[MySQL] Transaction Isolation
Junhong Kim 2025. 3. 2. 22:53Transaction Isolation
Transaction Isolation은 데이터베이스에서 여러 트랜잭션이 동시에 실행될 때, 한 트랜잭션이 다른 트랜잭션의 변경 사항을 언제, 어떻게 볼 수 있는지를 결정하는 규칙입니다. 그리고 Transaction Isolation은 여러 트랜잭션이 동시에 실행될 때 서로의 작업에 영향을 주지 않도록 하는 역할을 합니다.
Transaction Isolation Level
Transaction Isolation Level은 ANSI SQL 표준으로 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE로 4가지가 있으며, MySQL에서 현재 세션에 설정된 Isolation Level을 확인하고 싶은 경우 다음 쿼리를 실행하면 됩니다. (참고로 MySQL InnoDB 스토리지 엔진은 기본적으로 트랜잭션 격리 수준으로 REPEATABLE_READ를 사용합니다.)
SHOW TRANSACTION ISOLATION LEVEL;
| Variable_name | Value |
| --------------------- | --------------- |
| transaction_isolation | REPEATABLE-READ |
Isolation Level에 따라 '격리 수준이 높다'와 '격리 수준이 낮다'라고 하는데, 이는 트랜잭션이 읽기 작업에서 어느 정도의 읽기 이상(Read Anomaly)을 방지하는지에 따라 결정됩니다. 즉, 더 높은 격리 수준은 동시에 실행되는 트랜잭션 간의 간섭을 더 철저하게 차단하여, 데이터의 일관성을 보장하는 대신 동시성 처리 성능이 저하될 수 있습니다. 참고로 읽기 이상은 Dirty Read, Non-repetable Read, Phantom Read가 있습니다. 본 포스팅에서는 격리 수준이 가장 낮은 READ UNCOMMITTED 부터, 격리 수준이 가장 높은 SERIALIZABLE에 대해 알아봅니다.
READ UNCOMMITTED
READ UNCOMMITTED의 특징은 트랜잭션1이 commit 되기 전에, 다른 트랜잭션(여기서는 트랜잭션2)에서 commit 되지 않은 데이터를 읽을 수 있습니다. 즉, 트랜잭션1 변경 내용이 commit/rollback 여부와 상관없이 다른 트랜잭션에게 보이게 되는데 이를 Dirty Read 라고 합니다. 가장 낮은 격리 수준으로 성능이 높을 수 있지만, 데이터의 일관성이 떨어질 위험이 있어 사용되지 않는 격리 수준입니다.

READ COMMITTED
READ COMMITED는 대부분 RDBMS의 기본 격리 수준입니다. READ COMMITTED의 특징은 트랜잭션1이 commit 되기 전에는, 다른 트랜잭션(여기서는 트랜잭션2)에서 데이터를 읽을 때 undo 영역에 백업된 레코드에서 데이터를 가져옵니다. 즉, READ COMMITED는 commit 된 데이터만 읽을 수 있도록 제한합니다. READ COMMITTED에서는 Dirty Read가 방지되어 기본적인 일관성이 유지됩니다.

다만, READ COMMITED에서도 문제가 있습니다. 앞서 이야기 한 것 처럼 commit 되기 전에는 undo 영역에 백업된 레코드에서 데이터를 가져오지만, commit 된 이후에는 commit 된 데이터를 읽어옵니다. 따라서, 트랜잭션1이 commit 되기 전/후로, 트랜잭션2에서 테이블 값을 두 번 읽게되면, commit 전 select 한 데이터와 commit 후 select 한 데이터가 다르다는 문제가 발생하는데, 이를 Non-Repetable Read라고 합니다. 즉, 같은 트랜잭션에서 조회한 값이 조회 시점에 따라 다를 수 있는 현상을 의미합니다. 이는 한 트랜잭션 내에서 같은 select 쿼리를 실행했을 때 항상 같은 결과를 가져와야하는 REPETABLE READ 정합성에 어긋납니다.

REPETABLE READ
REPETABLE READ는 MySQL InnoDB 스토리지 엔진의 default 격리 수준 입니다. REPETABLE READ의 특징은 트랜잭션 시작 시점에 읽은 데이터는 트랜잭션 종료시까지 동일하게 유지됩니다. 이러한 정합성이 유지될 수 있는 이유는 MySQL에서 트랜잭션마다 트랜잭션 ID를 부여하여, 현재 트랜잭션 ID 보다 작은 트랜잭션 번호에서 변경한 데이터만 읽기 때문입니다. undo 공간에 데이터를 백업해두고 실제 레코드 데이터를 변경하며, 백업된 데이터는 불필요하다고 판단되는 시점에 주기적으로 삭제합니다. 참고로 undo 공간에 백업된 레코드가 많아지면 MySQL 서버의 처리 성능이 떨어질 수 있습니다.

다만, REPETABLE READ에서도 문제가 있습니다. PHANTOM READ라는 현상이 발생할 수 있는데, 이는 한 트랜잭션에서 같은 조회 쿼리르 두 번 수행할 때, 첫 번째 쿼리에서 없던 쿼리가 두 번째 쿼리에서 나타나는 것을 의미합니다. 이러한 현상이 발생할 수 있는 이유는 트랜잭션 도중 새로운 레코드가 삽입되는 것을 허용했기 때문입니다. REPETABLE_READ 격리 수준에서는 공유 잠금(shared lock)의 상태의 데이터에 대해 변경 불가가 보장되지만, 데이터를 변경시키지 못할 뿐 새로운 데이터를 추가/삭제하는 것은 가능합니다. 따라서 이를 PHANTOM READ라고 부릅니다.

SERIALIZABLE
SERIALIZABLE은 가장 높은 격리 수준이자 단순한 격리 수준입니다. SERIALIZABLE은 모든 트랜잭션이 순차적으로 실행된 것처럼 처리됩니다. SERIALIZABLE에서는 select 쿼리가 전부 LOCK IN SHARE MODE로 자동 변경됩니다. LOCK IN SHARE MODE는 select를 한 뒤에 트랜잭션이 끝날 때까지 해당 row 값이 변경되지 않는 것을 보장합니다. SERIALIZABLE에서는 PHANTOM READ도 발생하지 않아 SERIALIZABLE은 모든 읽이 이상 문제가 방지되지만, 동시 실행 성능이 크게 저하될 수 있습니다. 따라서, DB 격리 수준으로 거의 사용되지 않습니다.
LOCK IN SHARE MODE
해당 row를 update 하거나 delete 하려는 쿼리는 잠김 상태가 되어 트랜잭션이 끝날 때까지 대기하게 됩니다. 하지만, select는 얼마든지 여러 세션이 동시에 수행되는 것이 가능합니다. 기존 select는 얼마든이 여러 세션에서 동시에 수행되는 것이 가능합니다. (참고로 트랜잭션이 끝나기 전까지만 유효하므로 auto commit을 꺼야 합니다).
요약
Isolation Level | Dirty Read | Non-Repetable Read | Phantom Read |
READ UNCOMMITTED | ✅ | ✅ | ✅ |
READ COMMITTED | ❌ | ✅ | ✅ |
REPETABLE READ | ❌ | ❌ | ✅ |
SERIALIZABLE | ❌ | ❌ | ❌ |
참고
'Database > MySQL' 카테고리의 다른 글
[MySQL] DBeaver 접속 오류 (0) | 2020.02.12 |
---|---|
[MySQL] MySQL character set encoding 변경 (utf8, utf8mb4) (0) | 2018.03.29 |
[MySQL] MySQL 완전 삭제 방법 (0) | 2018.03.29 |
[MySQL] MySQL 비밀번호 변경 방법 (4) | 2018.03.29 |
[MySQL] MacOS에서 MySQL 설치 (with. homebrew) (0) | 2018.03.29 |