2개 이상의 트랜잭션이 동시에 처리할 경우 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 할지 말지를 결정하는 수준
아래와 같이 4개의 수준으로 나뉘며 아래로 갈수록 동시성이 떨어지고 격리 수준이 높아진다.
1. READ UNCOMMITTED(DIRTY READ)
2. READ COMMITTED
3. REPEATABLE READ
4. SERIALIZABLE
DIRTY READ | NON-REPEATABLE READ | PHANTOM READ | |
---|---|---|---|
READ UNCOMMITTED | 발생 | 발생 | 발생 |
READ COMMITTED | X | 발생 | 발생 |
REPEATABLE READ | X | X | 발생(InnoDB는 없음) |
SERIALIZABLE | X | X | X |
각 트랜잭션에서의 변경 내용이 COMMIT, ROLLBACK 여부에 상관없이 다른 트랜잭션에서 보인다.
READ UNCOMMITTED에서는 Dirty read가 허용된다. 정합성에 문제가 많기 때문에 RDBMS 표준에선 격리 수준으로 인정하지 않는다.
어떤 트랜잭션에서 데이터를 변경했더라도 COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있기 때문에 Dirty read 현상은 발생하지 않는다.
사용자 A는 id=2인 사원의 이름을 LEE -> PARK로 변경했다. 이 때 새로운 값인 PARK는 EMPLOYEES 테이블에 기록되고 이전 값인 LEE는 UNDO 영역으로 백업된다.
사용자 A가 commit하기 전, 사용자 B가 id=2인 사원을 조회하면 이름의 값은 LEE로 조회된다. (여기서 가져온 결과는 UNDO에서 백업된 레코드 값을 가져온 것)
READ COMMITTED 레벨에서는 어떤 트랜잭션에서 변경한 내용이 커밋되기 전까지는 다른 트랜잭션에서 그 변경 내역을 조회할 수 없다. 최종적으로 변경 내역을 커밋하고 나서 다른 트랜잭션에서도 새롭게 변경된 값을 참조할 수 있다.
하지만, 이 격리 수준에서도 NON-REPEATABLE READ 문제가 발생한다.
InnoDB 엔진은 트랜잭션이 ROLLBACK될 가능성에 대비해 변경되기 전 레코드를 UNDO 영역에 백업해두고 실제 레코드 값을 변경한다.(MVCC, Multi Version Concurrency Control)
이 격리 수준은 MVCC를 위해 언두 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있도록 보장한다. (READ COMMITTED도 MVCC를 이용해 커밋되기 전의 데이터를 보여준다.)
REPEATABLE READ, READ COMMITTED의 차이
UNDO 영역에 백업된 레코드의 여러 버전 가운데 몇번째 이전 버전까지 찾아들어가야 하느냐의 차이가 있다.
- REPEATABLE READ: MVCC를 보장하기 위해 실행 중인 트랜잭션 가운데 가장 오래된 트랜잭션 번호보다 트랜잭션 번호가 앞선 언두 영역의 데이터는 삭제할 수 없다.
-> 즉 특정 트랜잭션 번호의 구간 내에서 백업된 언두 데이터가 보존되어야 한다.
모든 InnoDB의 트랜잭션은 고유한 트랜잭션 번호를 가지며 UNDO 영역에 백업된 모든 레코드에는 변경을 발생시킨 트랜잭션의 번호가 포함되어 있다.
EMPLOYEES 테이블은 트랜잭션 6번에 의해 INSERT 되었다.
사용자 A의 트랜잭션 번호는 12, B의 트랜잭션 번호는 10
사용자 A는 이때, name을 PARK로 변경하고 COMMIT을 하였고 사용자 B는 같은 SELECT 쿼리에도 같은 결과를 반환받았다.
10번 트랜잭션 안에서 실행되는 모든 SELECT 쿼리는 트랜잭션 번호가 10보다 작은 트랜잭션 번호에서 변경한 것만 볼 수 있기 때문이다.
BUT, 이 격리 수준에서도 PHANTOM READ 부정합이 발생할 수 있다.
사용자 A가 INSERT 수행 도중, 사용자 B가 SELECT 쿼리로 조회했을 때 서로 다른 결과를 얻게 된다. SELECT ... FOR UPDATE나 SELECT ... LOCK IN SHARE MODE로 조회되는 레코드는 UNDO 영역의 변경 전 데이터를 가져오는 것이 아니라 현재의 값을 가져오게 되는 것이다.