데이터 베이스는 여러 사용자가 동시에 접근할 수 있는 프로그램이다. 여러 사용자의 동시성 제어를 하기위해 트랜잭션이라는 하나의 작업단위 셋을 제공하는데 격리수준이란 여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것이다.
트랜잭션 격리가 제대로 이루어지지 않을때 보편적으로 발생할 수 있는 문제는 대표적으로 아래 4가지를 확인할 수 있다.
갱신 손실(LOST UPDATE)
A, B 트랜잭션이 동시에 같은 정보를 업데이트할 경우 최후에 반영한 트랜잭션 B에 값이 DBMS에 적용이 되고 A 트랜잭션이 변경한 값은 없어지는 경우
더티 읽기(DIRTY READ): A 트랜잭션에서 변경 후 커밋되지 않은 변경 내역을 B 트랜잭션에서 읽는 경우
반복 불가능한 읽기(NON-REPEATABLE READ): A 트랜잭션에서 하나의 데이터 항목을 시작과 종료시 두번 읽는 사이에 B 트랜잭션에 의해 해당 데이터 항목이 변경되고 커밋된다면 A 트랜잭션이 종료시에는 읽은 데이터 항목은 시작시 읽은 데이터 항목 값과 달라 데이터 정합성에 문제가 있는 경우
팬텀읽기(PHANTOM READ): 반복 불가능한 읽기와 비슷한 문제로 특정 로우의 값이 아닌 전체 데이터 개수, 합계와 같이 읽을때마다 값이 바뀌는 경우
이러한 격리수준은 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE이 있으며 오라클은 READ COMMITTED, MySQL은 REPEATABLE READ이 기본 값으로 사용한다.
| 격리수준 | 한글 명칭 | 설명 |
|---|---|---|
| READ UNCOMMITTED | 커밋되지 않은 읽기 격리 | 커밋되지 않은 데이터를 읽을 수 있는 가장 낮은 수준의 격리 |
| READ COMMITTED | 커밋된 읽기 격리 | 커밋된 데이터만 읽을 수 있는 격리 |
| REPEATABLE READ | 반복 가능한 읽기 격리 | 같은 트랜잭션 내에서 동일한 데이터를 읽을 때 항상 동일한 결과를 보장 |
| SERIALIZABLE | 직렬화 가능 격리 | 모든 트랜잭션을 직렬화하여 처리. 가장 높은 수준의 격리 |
격리 수준에 따른 문제 발생 여부
| 격리수준 | 갱신 손실 | 더티 읽기 | 반복 불가능한 읽기 | 팬텀읽기 |
|---|---|---|---|---|
| READ UNCOMMITTED | 발생 | 발생 | 발생 | 발생 |
| READ COMMITTED | 없음 | 없음 | 발생 | 발생 |
| REPEATABLE READ | 없음 | 없음 | 없음 | 발생 (InnoDB는 없음) |
| SERIALIZABLE | 없음 | 없음 | 없음 | 없음 |
특정 트랜잭션에서 데이터 변경이 이뤄졌을때 commit, rollback 여부와 상관없이 변경된 내역을 조회하는 격리 수준이다.
해당 설정에 문제점은 다른 사용자가 변경된 데이터를 읽어 사용했는데 해당 데이터가 적재되는 과정에서 문제가 있어 rollback 되는 경우이다. 이러한 경우 없는 데이터를 다른 사용자가 사용하게 됨으로 문제가 된다. 이처럼 트랜잭션 처리가 되지 않았지만 다른 트랜잭션에서 볼 수 있는 현상을 더티 리드(Dirty read) 라고 한다.
결국은 해당 격리수준은 문제가 되는 경우가 많아 사용하는 것을 권장하지 않는다.
온라인 서비스에서 가장 많이 선택되는 격리 수준으로 특정 트랜잭션에서 변경된 내용에 대해 commit이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있는 격리수준이다.
이때 변경한 트랜잭션을 A라 하고 변경된 내역을 조회하는 B라고 한다면 A가 변경한 내역은 테이블에 적용과 동시에 이전 값은 언두로그에 저장된다.
다음 B에서 A가 변경한 내역을 조회하면 언두 로그에 값을 읽어오게 되는 것이다.
다만 READ COMMITTED에 문제는 NON-REPEATABLE READ 문제가 발생한다는 것이다.
InnoDB 스토리지 엔진에서 기본으로 사용되는 격리 수준으로 데이터의 값이 변경되면 언두로그에 이전 값을 저장하고 있는데 이러한 언두로그를 이용해서 한 트랜잭션내에서 동일한 값을 반환하는 방법이다.
또한 InnoDB 스토리지 엔진은 갭 락과 넥스트 키 락 덕분에 PHANTOM READ 문제가 발생하지 않는다.
다만 REPEATABLE READ는 SELECT...FOR UPDATE와 같은 락을 걸고 현재 레코드 조회를 사용할 경우에 다른 커넥션에서 삽입하고 커밋한 내용도 락을 걸고 같이 조회해오게 되어 PHANTOM READ 문제가 발생한다.
InnoDB 테이블은 기본적으로 읽기 작업에 대해서는 락을 걸지 않지만 해당 격리수준에서는 공유 잠금(읽기 잠금)을 획득해야하며 다른 트랜잭션에서는 해당 레코드를 읽거나 변경하지 못하게 된다.