격리성(Isolation)은 트랜잭션 수행 시 서로(다른 트랜잭션이) 끼어들지 못하는 것을 말합니다.
A, B, C, D라는 트랜잭션이 있을 때 각 트랜잭션이 15초가 걸린다고 가정해봅시다.
격리성을 높혀서 어떠한 트랜잭션도 끼어들지 못하게 만든다면 4개의 다른 트랜잭션을 하나씩 실행해서 총 60초가 걸릴 것입니다.
격리성을 살짝 낮춰서 2개씩 병렬적으로 실행시키면 4개의 트랜잭션을 처리하는데 30초가 걸릴 것입니다.
만약 4개의 트랜잭션을 한 번에 처리해버리면 15초가 걸리게 할 수도 있을 것입니다.
상황 성능 A -> B -> C -> D 60(15+15+15+15)초 A -> C
B -> D30(15+15)초 4개를 한 번에 실행 15초
격리성이 높혀서 하나씩 처리할 경우 60초로 가장 느리게 처리했습니다.
격리성을 가장 낮춰서 4개를 한 번에 처리하는 경우에는 15초로 가장 빠르게 처리했습니다.
격리성을 낮출 수록 동시에 처리하는 양, 즉 동시성이 높아집니다.
격리성과 동시성은 반비례 관계인 것입니다.
(동시에 처리하는 양이 많다 = 동시성이 높다 = 성능이 좋다)
Q. 그러면 격리성을 낮춰서 처리하는게 성능이 가장 좋으니까 격리성을 낮추는게 무조건 좋겠다.
A. 격리성을 낮출 수록 이상현상이 발생할 확률이 높아지기 때문에 상황에 따라 다릅니다.
A -> B 500만원 이체 A계좌 100만원 출금 트랜잭션 시작 A계좌 잔액 조회(A계좌 잔액 : 500만원) A계좌 잔액 : 500만원 - 100만원 A계좌 잔액 조회(A계좌 잔액 : 400만원) 그냥 안할래. Rollback! 계좌의 돈이 500만원보다 적은데? 실패! 이상현상 예시
격리성을 낮출수록 이상현상이 발생할 확률이 높아진다 했는데 어떤 이상현상들이 있는지 알아보겠습니다.
이상현상이 "무조건 일어난다"가 아니라 "일어날 수도 있다"입니다.
더티 리드(Dirty Reads)란 커밋되지 않은 다른 트랜잭션의 데이터가 조회되는 현상을 말합니다.
사직서 제출을 고민하는 직원 A
와 사원의 수만큼 초코파이를 준비해야 하는 막내
가 있는 상황을 가정해봅시다.
사원 테이블은 Employee
를 사용하고 사원은 직원 A
와 막내
포함 총 5명이 있습니다.
직원 A
트랜잭션 : 사직서를 쓸까...?
Delete From Employee where id = '직원 A';
막내
트랜잭션 : 사원들을 전부 조회해보자.
Select * From Employee;
세보니 4명이구나. 4개 준비하자.
직원 A
트랜잭션 (중요) : 그냥 계속 다니자.
Rollback;
막내
트랜잭션 : 아까 조회했을 때 직원 수가 4명이었나?
Select * From Employee;
5명이었어?
Employee 테이블에 직원 A
의 데이터가 지워지지 않았습니다.(= 커밋된 적이 없습니다.)
하지만 막내
는 직원 A
커밋되지 않은 트랜잭션의 데이터가 조회되었습니다.
반복 불가능한 읽기(Non-Repeatable Reads)란 같은 행을 다시 조회했는데 값이 달라지는 것을 말합니다.
이사를 가는 직원 A
와 직원 A의 주소로 초코파이를 보내야 하는 막내
가 있는 상황을 가정해봅시다.
직원 A
의 주소는 이전 주소는 100동 100호
입니다.
막내
트랜잭션 : 직원 A를 조회하자.
Select * From Employee Where id = '직원 A';
100동 100호로 보내면 되겠다.
직원 A
트랜잭션 : 이사를 갔으니까 테이블을 최신화해야지
Update Employee Set address = '101동 101호' where id = '직원 A';
Commit;
막내
트랜잭션 : 확실하게 보내야하니까 다시 조회해보자.
Select * From Employee Where id = '직원 A';
101동 101호? 음...
팬텀리드(Phantom Reads)란 한 트랜잭션 내에서 동일한 쿼리를 2번 이상 보냈을 때 해당 조회 결과가 다른 것을 의미합니다.
이번에는 새롭게 입사한 직원 K
와 다시 사원의 수만큼 초코파이를 준비해야 하는 막내
가 있는 상황을 가정해봅시다.
막내
는 이번에 Count
라는 집계함수를 배웠습니다.
막내
트랜잭션 : 사원들을 전부 조회해보자.
Select Count(*) From Employee;
세보니 5명이구나. 5개 준비하자.
직원 K
트랜잭션 : 새롭게 입사했으니 내 데이터를 직원 테이블에 넣어야지.
Insert Into Employee (id, address) VALUES ('직원 K', '1100동 1010호');
Commit;
막내
트랜잭션 : 직원 수 5명 맞지?
Select Count(*) From Employee;
6명이야?
Q. 팬텀리드, 반복 불가능한 읽기는 모두 두 번의 조회로 조회 결과가 달라지는데 같은 거 아니야?
A. 반복 불가능한 읽기는 행(Row)에 초점을 두고 있습니다. 예시에서도직원 A
를 콕 찝어서 검색하고 있습니다.
하지만 팬텀리드는 행이 아닌 전체적인 데이터에 초점을 두고 있습니다.
앞서 살펴본 3가지 이상현상들은 격리수준에 따라 발생할 확률이 있습니다.
여기서는 격리성이 가장 높은 Serializable부터 격리성이 가장 낮은 Read Uncommitted까지 살펴보며 어떤 이상현상이 발생할 수 있는지 살펴보겠습니다.
Serializable는 커밋 완료된 데이터에 대해서만 조회할 수 있으며 트랜잭션을 순차적으로 진행시키는 격리수준입니다.
각각 15초가 소요되는 A, B, C, D 트랜잭션을 하나씩 실행시켜 60초가 걸리는 것과 같습니다.
하나씩 실행하기 때문에 성능은 좋지 않지만 이상현상이 일어나지 않습니다.
Repeatable Read는 커밋 완료된 데이터에 대해서만 조회할 수 있으며, 하나의 트랜잭션에서 반복해서 행을 조회하더라도 똑같은 행을 보장하는 격리수준입니다.
Employee라는 테이블이 있다고 가정할 때 '트랜잭션 A'에서 11번 직원을 검색했다고 가정해봅시다.
이때 '트랜잭션 B'가 11번 직원을 업데이트하려고 시도한다면,
다음과 같이 '트랜잭션 A'가 종료되길 기다려야 합니다.
트랜잭션 A-1 :
Select * From Employee Where id = 11
실행
트랜잭션 B-1 :Update Employee Set name = '막내' Where id = 11;
실행
트랜잭션 B-2 : 원하는 작업을 다 하고Commit;
트랜잭션 A-2 :Select * From Employee Where id = 11
실행
다시 조회해도 트랜잭션 B에서 업데이트한 데이터가 반영되지 않는다.
하지만 Repeatable Read 격리수준에서는 테이블에 새로운 데이터를 삽입하는 것을 막아주진 않습니다.
(B-1
에서 Insert Into Employee ...
는 가능하다는 의미)
그렇기 때문에 팬텀리드 현상이 발생할 수 있습니다.
MySQL 8.0 Inno DB의 기본 격리수준입니다.
Read Committed 커밋 완료된 데이터에 대해서만 조회할 수 있는 격리수준입니다.
Repeatable Read에서 살펴본 상황이 Read Committed 격리수준에서 발생한다면 트랜잭션 B-1은 대기하지 않고 실행됩니다.
트랜잭션 A-1 :
Select * From Employee Where id = 11
실행
트랜잭션 B-1 :Update Employee Set name = '막내' Where id = 11;
실행
트랜잭션 B-2 : 원하는 작업을 다 하고Commit;
트랜잭션 A-2 :Select * From Employee Where id = 11
실행
트랜잭션 B에서 업데이트한 데이터가 보이며 반복 불가능한 읽기(Non-Repeatable Reads) 발생
Read Committed에서는 커밋 완료된 데이터가 보이기 떄문에 반복 불가능한 읽기(Non-Repeatable Reads)가 발생할 수 있습니다.
그리고 Repeatable Read처럼 Insert
를 막지 않기 때문에 팬텀리드 또한 발생할 수 있습니다.
Oracle, PostgreSQL의 기본 격리수준입니다.
Read Uncommitted는 다른 트랜잭션이 커밋하지 않은 정보를 읽을 수 있습니다.
다른 트랜잭션이 커밋하지 않은 정보를 읽을 수 있기 때문에
팬텀리드, 반복 불가능한 읽기(Non-Repeatable Reads), 더티 리드가 모두 발생할 수 있습니다.
MongoDB의 기본 격리수준입니다.
격리수준에 따라 발생할 수 있는 이상현상을 정리하자면 다음과 같습니다.
사진 출처 - [DATABASE] 트랜잭션의 격리 수준이란?
각각의 MySQL, Oracle, MongoDB 등등 각각의 DB마다 격리수준이 다르지만 설정을 통해 격리수준을 바꿀 수 있습니다.
각각의 격리수준과 격리수준에 따라 발생할 수 있는 이상현상들을 이해하고 프로젝트와 맞는 격리수준을 설정하면 됩니다.