MySQL InnoDB의 기본 트랜잭션 격리수준은?
테마
동시에 실행되는 트랜잭션이 서로의 변경을 어디까지 볼 수 있는가를 정하는 규칙. 격리를 강하게 할수록 이상 현상은 줄고 동시성은 떨어진다.
트랜잭션을 완전히 한 줄로 세워 실행하면 안전하지만 느리다. 반대로 아무 제약 없이 섞어 돌리면 커밋되지 않은 값을 읽거나, 같은 행을 두 번 읽었는데 값이 달라지는 일이 생긴다. 격리수준은 이 둘 사이에서 어떤 이상 현상까지 허용할지를 고르는 다이얼이다. SQL 표준(SQL-92)이 4단계를 정의하고, 엔진마다 실제 동작은 조금씩 다르다.
| 격리수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | 발생 | 발생 | 발생 |
| READ COMMITTED | 방지 | 발생 | 발생 |
| REPEATABLE READ (InnoDB 기본) | 방지 | 방지 | 표준은 허용, InnoDB는 대부분 방지* |
| SERIALIZABLE | 방지 | 방지 | 방지 |
* 표준 정의상 REPEATABLE READ는 Phantom Read를 허용하지만, InnoDB는 Gap Lock(Next-Key Lock)으로 범위 안 삽입을 막아 대부분 방지한다. 면접에서 자주 파고드는 지점.
세 이상 현상은 이렇게 구분한다.
REPEATABLE READ였다면 T1은 첫 SELECT 시점의 스냅샷을 계속 보므로 두 번째도 100이 나온다.
SELECT ... FOR UPDATE, FOR SHARE, UPDATE, DELETE 같은 잠금 읽기는 스냅샷이 아니라 최신 커밋 값을 읽는다. "REPEATABLE READ인데 왜 최신 값이 보이지?"의 답이 대부분 여기 있다.SELECT를 FOR SHARE처럼 바꿔 읽기에도 공유 잠금을 건다. 동시성이 크게 떨어져 실무에서는 거의 쓰지 않는다.-- 현재 세션 / 서버 기본값 확인 (MySQL 8: tx_isolation 아님)
SELECT @@SESSION.transaction_isolation, @@GLOBAL.transaction_isolation;
-- 다음에 시작하는 한 트랜잭션에만 적용 (SESSION/GLOBAL 생략)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- ...
COMMIT;
-- 이 커넥션 전체에 적용
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;| 엔진 | 기본값 | 메모 |
|---|---|---|
| MySQL InnoDB | REPEATABLE READ | Gap Lock으로 Phantom 대부분 방지 |
| PostgreSQL | READ COMMITTED | REPEATABLE READ는 스냅샷 격리. 갱신 충돌 시 에러로 실패시킴 |
| Oracle | READ COMMITTED | READ UNCOMMITTED·REPEATABLE READ 없음. SERIALIZABLE은 스냅샷 격리 |
| SQL Server | READ COMMITTED | 기본은 잠금 기반. 스냅샷 격리는 옵션 |
"기본 격리수준이 뭐냐"는 질문에 MySQL만 REPEATABLE READ라는 점을 짚어 주면 좋다.
오해 1 — "REPEATABLE READ면 Phantom Read는 무조건 발생한다"
표준 정의는 그렇지만 InnoDB는 Next-Key Lock으로 대부분 막는다. 반대로 "InnoDB에서는 절대 안 난다"도 틀렸다. 스냅샷 읽기만 하던 트랜잭션이 중간에 UPDATE를 하면, 그 문장은 최신 커밋 값을 대상으로 하므로 다른 트랜잭션이 삽입한 행이 갱신 대상에 들어오고, 이후 스냅샷에 그 행이 보이게 된다.
오해 2 — "격리수준을 올리면 갱신 손실(Lost Update)이 해결된다"
재고 차감처럼 "읽고 → 판단하고 → 쓰는" 패턴은 REPEATABLE READ에서도 두 트랜잭션이 같은 스냅샷을 보고 둘 다 차감할 수 있다. 격리수준은 읽기 일관성 도구다. 갱신 충돌은 SELECT ... FOR UPDATE, 조건부 UPDATE ... WHERE stock > 0, 낙관적 버전 컬럼으로 푼다.
기억법
격리수준 4개는 "이상 현상 3개를 위에서부터 하나씩 끈다"로 외운다. Dirty → Non-Repeatable → Phantom.
@Transactional(isolation = ...) 은 결국 SET TRANSACTION ISOLATION LEVEL을 커넥션에 실행하는 것이다. 커넥션 풀을 쓰면 반납 시 원복되는지 확인한다.면접에서 이렇게 묻는다
"격리수준 설명해 주세요" → "MySQL 기본값은요?" → "그럼 Phantom Read는 InnoDB에서 발생하나요?" → "그러면 재고 차감은 어떻게 막나요?" 순으로 파고든다. 표 하나와 예외 두 개를 말할 수 있으면 통과.
MySQL InnoDB의 기본 트랜잭션 격리수준은?
InnoDB REPEATABLE READ에서 Phantom Read를 막는 데 쓰이는 잠금은?
현재 세션의 격리수준을 조회하는 MySQL 8 구문
SELECT @@.transaction_isolation;