들어가며
본 포스팅에선 트랜잭션 격리 수준을 조절하지 않으면 어떤 문제가 발생할 수 있는지, 그러한 문제들을 해결하고자 트랜잭션 격리 수준을 어떻게 설정하면 좋을 지에 대해 정리합니다.
❏ 트랜잭션 격리 수준
트랜잭션의 격리 수준(Isolation Level)이란 동시에 여러 트랜잭션이 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를, 볼 수 있게 허용할지 말지를 결정하는 것입니다.
❍ 필요성
트랜잭션이 동시에 실행될 때 데이터의 일관성을 지키기 위해서는 두 가지 방법이 있습니다.
1. DB Lock
한 트랜잭션이 데이터를 사용할 때 다른 트랜잭션의 접근을 막는 방법이다. 하지만 특정 테이블이나 row에 대해 Lock을 걸어 아무도 접근하지 못하도록 하다보니, 순차적으로 작업이 수행되어야 하므로 성능이 매우 떨어집니다.
2. 트랜잭션 격리 수준 사용
격리 수준을 조절하면서 데이터베이스에 적절한 전략을 사용하여 성능과 일관성의 균형을 맞출 수 있다.
❍ 발생할 수 있는 문제들
1. Dirty Read
한 트랜잭션이 다른 트랜잭션의 아직 커밋되지 않은 변경사항을 읽을 수 있는 현상입니다.
-- Transaction B
UPDATE people SET age = 30 WHERE id = 1;
-- (아직 커밋하지 않음)
-- Transaction A
SELECT age FROM people WHERE id = 1;
- 트랜잭션 B가 값을 수정합니다.
- 트랜잭션 A가 트랜잭션 B가 수정한 값을 읽습니다. (Dirty Read!)
- 트랜잭션 B가 이후 롤백되면서 트랜잭션 A는 일관성 없는 데이터를 가지게 됩니다.
2. Non-Repeatable Read
한 트랜잭션이 같은 데이터를 여러 번 읽을 때, 그 사이 다른 트랜잭션에 의해 데이터가 수정되는 현상입니다.
-- Transaction A
SELECT age FROM people WHERE id = 1; -- 20 조회
-- Transaction B
UPDATE people SET age = 30 WHERE id = 1;
COMMIT;
-- Transaction A
SELECT age FROM people WHERE id = 1; -- 30 조회
- 트랜잭션 A가 값을 읽습니다.
- 그 후 트랜잭션 B가 해당 값을 수정합니다.
- 트랜잭션 A가 다시 값을 읽을 때 처음과 다른 값이 조회됩니다.
3. Phantom Read
한 트랜잭션이 조건에 맞는 여러 행을 읽었는데, 그 사이 다른 트랜잭션에 의해 행이 삽입되어 조회되는 결과가 달라지는 현상입니다.
-- Transaction A
SELECT * FROM people WHERE age BETWEEN 20 AND 30;
-- Transaction B
INSERT INTO people VALUES ('John', 25);
COMMIT;
-- Transaction A
SELECT * FROM people WHERE age BETWEEN 20 AND 30;
-- 이전과 다른 결과 집합이 조회됨
- 트랜잭션 A가 특정 조건에 맞는 레코드들을 조회합니다.
- 그 후 트랜잭션 B가 새로운 레코드를 삽입합니다.
- 트랜잭션 A가 다시 조회하면 레코드가 추가되어 처음과 다른 결과가 조회됩니다.
❏ 격리 수준 종류
❍ READ UNCOMMITTED (레벨 0)
READ UNCOMMITTED는 커밋되지 않은 데이터를 다른 트랜잭션이 조회할 수 있도록 허용하는 격리 수준입니다.
- 가장 낮은 격리 수준입니다.
- Dirty Read, Non-Repeatable Read, Phantom Read 모두 발생할 수 있습니다.
- 성능은 좋겠지만, 데이터 정합성을 보장할 수 없다라는 큰 단점이 있습니다.
❍ READ COMMITTED (레벨 1)
READ COMMITTED 는 트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리수준입니다.
- Dirty Read 해결할 수 있습니다.
- Non-Repeatable Read, Phantom Read 모두 발생할 수 있습니다.
❍ REPEATABLE READ (레벨 2)
REPEATABLE READ 는 같은 트랜잭션 내에서 조회한 데이터의 값이 항상 동일함을 보장하는 격리 수준이다.
- Dirty Read, Non-Repeatable Read 해결할 수 있습니다.
- Phantom Read 발생할 수 있습니다.
❍ SERIALIZABLE (레벨 3)
SERIALIZABLE는 트랜잭션의 읽기 작업 시에도 공유 Lock을 획득하게 함으로써, 동시에 다른 트랜잭션이 같은 데이터에 접근할 수 없도록 하는 격리 레벨입니다.
- 가장 높은 격리 수준입니다.
- 모든 문제가 발생하지 않습니다.
- 성능이 가장 좋지 않습니다.
- 동시성이 제일 낮습니다.
❏ 마무리
높은 격리 수준(SERIALIZABLE )을 사용할 수록, 동시성이 떨어집니다. 또한, DeadLock이 발생할 가능성이 높아집니다.
낮은 격리 수준을 사용할 수록, 데이터 무결성에 문제가 발생할 수 있습니다.