개요
DB에 관심을 갖고 테코톡과 Real MySQL을 읽으며, 트랜잭션과 동시성 처리 등에 관심을 갖게 되었다. 그 중 가장 어렵다고 느낀 트랜잭션 격리 수준에 대한 개념을 정리하려 한다.
트랜잭션 격리 수준은 4단계로 나누어지며, 각 단계 별 부정합 문제점들을 갖고 있어 뒤로 갈 수록 갖고 있던 문제점들을 해결해 나간다.
1. READ UNCOMMITTED
각 트랜잭션의 변경내용이 COMMIT 혹은 ROLLBACK 여부에 상관없이 다른 트랜잭션에 노출된다.
예시

- 사용자 A가 트랜잭션을 시작함과 동시에 emp_no = 50000 인 사원을 INSERT 후 COMMIT은 하지 않는다.
- 하지만 사용자 B가 테이블 SELECT 시, emp_no = 50000 인 사원이 조회된다.
트랜잭션이 COMMIT 되기도 전에 이미 데이터는 테이블에 들어갔다❗
다음과 같은 현상을 Dirty read 라고 한다.
Dirty read란?
어떤 트랜잭션에서 처리한 작업이 완료되지 않았지만, 다른 트랜잭션에서 볼 수 있는 현상
Dirty read가 해결되지 않으면, 트랜잭션 내에서 COMMIT 되지 않아도 데이터를 쓰고 지우는 모든 작업들이 테이블에 그대로 드러나게되어 혼란스럽게 될 것이다. 동시성은 가장 크지만, 데이터의 정합성이 크게 어긋나기에 해당 수준은 사용하지 않는다고 한다.
2. READ COMMITTED
어떤 트랜잭션에서 데이터를 변경했더라도 COMMIT된 데이터만 다른 트랜잭션에서 조회 가능
예시

- 사용자 A가 emp_no = 50000인 사원의 first_name을 UPDATE 한다.
- 이전 값은 언두 영역으로 백업
- 사용자 A가 COMMIT 전, 사용자 B가 emp_no = 50000인 사원 조회
- 사용자 B의 조회 결과는 언두 영역에 저장 된 데이터로 조회
COMMIT된 데이터만 조회 가능하기에, Dirty read 문제점은 해결할 수 있지만 다음과 같은 문제점이 있다.
문제점

- 사용자 B가 트랜잭션을 시작하고, first_name = 'Toto' 인 데이터를 조회하였지만 해당 데이터는 존재하지 않는다.
- 그 후 사용자 A가 바로 first_name = 'Toto' 인 데이터를 INSERT 후 COMMIT한다.
- 사용자 B가 트랜잭션 종료 전 first_name = 'Toto' 인 데이터를 조회하면, 해당 데이터는 존재한다.
사용자 B의 입장에서 보면, 한 트랜잭션 내에서 같은 조회 쿼리를 날렸지만 다른 결과가 반환 되었다❗
다음과 같은 현상을 NON-REPEATABLE READ 라고 한다.
NON-REPEATABLE READ 란?
하나의 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때, 항상 같은 결과를 가져와야 하는 정합성(REPEATABLE READ)에 어긋나는 것
3. REPEATABLE READ
언두 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있게 보장
각 트랜잭션에 번호가 존재하며, 언두 영역에 존재하는 백업 데이터 중, 데이터의 트랜잭션의 번호보다 낮은 버전을 조회한다.
예시

- 사용자 A의 트랜잭션의 번호는 12, 사용자 B의 트랜잭션의 번호는 10
- 사용자 B가 10 트랜잭션을 시작하고, emp_no = 50000인 레코드를 조회한다. -> 'Lara' 조회
- 그 후 사용자 A가 12 트랜잭션에서 first_name = ‘Toto’로 변경 후 COMMIT한다.
- 사용자 B가 같은 10 트랜잭션 안에서 emp_no = 50000인 레코드를 조회한다. -> 언두 로그의 'Lara' 조회
다른 트랜잭션에 의해 테이블이 변경되어도, 언두 로그를 이용하여 변경되기 전 데이터를 조회하기에
NON-REPEATABLE READ는 해결할 수 있지만, 다음과 같은 문제점이 있다.
문제점

- 사용자 B가 'SELECT…FOR_UPDATE'를 통하여 emp_no ≥ 50000인 데이터를 조회한다 -> 결과 1건 조회, 해당 레코드 Lock
- 사용자 A가 새로운 데이터를 INSERT 후 COMMIT 한다.
- 사용자 B가 'SELECT…FOR UPDATE'를 통하여 emp_no ≥ 50000인 데이터를 조회한다 -> 결과 2건 조회
다른 트랜잭션에서 수행한 INSERT 작업에 의해 레코드가 유령처럼 갑자기 보이게 되었다 ❗
다음과 같은 현상을 PHANTOM READ 라고 한다.
'SELECT…FOR_UPDATE' -> 해당 레코드를 조회함과 동시에 쓰기 잠금을 걸게 된다.
설명한대로 REPETABLE READ 단계에서 조회 시 변경이 있다면 언두 영역을 조회해야 한다.
하지만 언두 영역에 레코드는 쓰기 잠금이 불가능하다.
때문에 실제 데이터베이스의 값을 갖고올 수 밖에 없으며, 이전에 조회되지 않았던 추가된 데이터를 갖고오게 된다.
PHANTOM READ
다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다 안 보였다 하는 현상
4. SERIALIZABLE
가장 엄격한 격리 수준
모든 부정합 문제를 해결하지만, 읽기 작업 시에도 읽기 잠금을 획득해야 하기에 동시 처리가 불가능하다.
결론
몇일 날 잡아서 파고들어 글로 정리하니 이해함과 동시에 잊혀지지 않을 것 같다. 직접 쓰레드 2개 돌려서 코드로도 실험을 해보려 호기롭게 도전해보았지만, 아직 그정도 능력은 되지 않았다...
추후에 동시성 처리에 대해 더 공부하고 자바 쓰레드에 대해 깊게 알게된다면, 해당 글을 코드로 구현해보고 포스팅 해보아야겠다!
'Database > RDB' 카테고리의 다른 글
| [Database] 인덱스를 통한 쿼리 성능 개선기 (1) | 2024.04.26 |
|---|