10. [BackEnd] transaction isolation level
Read Phenomena
읽기 이상 현상이란 트랜잭션을 사용할 때 발생할 수 있는 이상 현상들
Dirty Read
동시에 진행되고 있는 다른 트랜잭션(아직 커밋하지 않은 상태)에서 변경한 데이터를 현재 진행 중인 트랜잭션에서 읽어 들이는 것
Non-repeatable Reads
하나의 트랜잭션 중 읽었던 특정 row의 값을 같은 트랜잭션 내에서 다시 읽어 들이는데 중간에 변경사항이 생겨 (실제로 COMMIT이 된 변경사항) 결괏값이 다르게 나오는 현상을 뜻합니다.
Phantom Reads
트랜잭션 시작 시점 데이터를 읽었을 때 존재하지 않았던 데이터가 다시 같은 조건으로 데이터를 읽어 들였을 때 존재해 (유령처럼) INCONSISTENT 한 결괏값을 반환하는 현상을 뜻합니다.
Serialization Anomaly
동시 커밋된 트랜잭션 그룹의 결과는 겹치지 않고 어떤 순서로든 순차적으로 실행하려고 하면 달성할 수 없습니다.
Isolation level
Read Uncommitted
트랜잭션이 격리 되지 않은 Isolation level
dirty read가 일어난다.
Read Committed
트랜잭션 내 모든 쿼리는 다른 트랜잭션을 통해 정상적으로 commit된 데이터만 볼 수 있다.
dirty read 해결
Repeatable Read
트랜잭션 중 특정 row를 읽어 들이는 쿼리를 실행한다면 해당 트랜잭션이 끝날 때까지 읽어들인 row의 데이터는 변하지 않음
Non-repeatable Read, Phantom Read 해결
Serializable
제일 엄격한 Isolation level이다. 트랜젝션이 진행되는 동안 특정 테이블을 읽으면 동시에 진행되는 다른 트랜잭션은 해당 테이블에 데이터를 추가, 변경, 삭제할 수 없음.
모든 Read Phenomena 해결
MySQL
Isolation level 확인
select @@transaction_isolation;
위 처럼 하면 isolation level을 볼 수 있습니다.
select @@global.transaction_isolation;
이렇게 하면 전역 레벨 확인 가능
Isolation 변경
set session transaction isolation level <Isolation 레벨>;
위가 기본 형태입니다.
read uncommitted
set session transaction isolation level read uncommitted;
위처럼 작성하면 read uncommitted로 isolation level을 변경합니다.
이렇게 하면 현재 세션의 transaction만 영향을 미칩니다.
read uncommitted에서는 Commit하지 않아도 변경된 값이 조회되는 현상이 있습니다. 이것이 dirty read라고 합니다.
read committed
set session transaction isolation level read committed;
read committed 레벨에서는 dirty read가 방지된다.
dirty read확인하는 예시 두 개의 같은 isolation level을 가진 세션을 열고 둘 다begin;으로 시작한다.하나는 account balance 업데이트를 하고 다른 하나에서 그 account를 조회할 때 update된 값인지 아닌지 확인하면 된다.
Non-repeatable Reads확인하는 예시 위 상황에서 2번 세션에서 account1에 대한 정보를 조회하고 1번 세션(account 변경한)에서Commit;한 후 다시 2번 세션에서 account1에 대한 정보를 조회하면 변경사항이 저장됐기 때문에 값이 달라져있습니다.같은 sql이더라도 다른 곳에서 커밋 때문에 다른 결과값이 나오는 이 현상을 Non-repeatable reads 라고 합니다.
Phantom Read확인하는 예시 위의 상황과 같습니다. 위의 상황에서 조건문을 통한selectsql을 날렸을 때 다른 곳에서 커밋으로 인해 값이 변경되어 조건에 만족되지 않는 순간 2번 세션 사용자 입장에서는 갑자기select결과가 3개 나오던 것이 2개만 나오는 현상입니다.
repeatable read
set session transaction isolation level repeatable read;
Non-repeatable Reads,Phantom Read가 방지됩니다. 세션 1에서 커밋을 해도 변경사항이 저장되지 않습니다.Serialization Anomaly확인하는 법 위의 상황을 모두 마치고 세션2에서 account1의 값을update한다면 세션1에서commit한 것이 적용되어서 세션2 입장에서는 다른 값으로update되는 것입니다. 이것이Serialization Anomaly입니다.
Serializable
set session transaction isolation level serializable;
이렇게하면 세션1에서 accounts 테이블을 조회하고 세션2에서 accounts 테이블을 조회하고 세션1에서 account를 업데이트하면 차단됩니다.
세션2의 SELECT 쿼리가 세션1의 UPDATE쿼리를 차단합니다.
이유는 isolation level이 serializable일 때 MySQL은 SELECT를 SELECT FOR SHARE로 변환한다고 합니다. 이러면 다른 트랜잭션은 읽기만 가능하고 UPDATE, DELETE를 할 수 없습니다.
만약 세션2에서 SELECT하고 세션1에서 UPDATE를 시도한 이후 세션2에서 Commit이나 Rollback을 하지 않았다면 에러가 나면서 트랜잭션을 다시 시도해야 한다고 한다.
이 때 트랜잭션 재시도 전략을 잘 짜야 한다고 하네요.
왜냐하면 교착상태에 빠질 수도 있어서( 서로 업데이트하려는 경우 )
그래서 한 쪽에서 update한다면 반대편에서 commit이 되도록 짜야한다.
Postgres
isolation level 확인
db 콘솔에서
show transaction isolation level;
기본적으로 read committed 입니다. (MySQL은 기본이 repeatable read임.)
isolation level 설정
postgres는 한 트랜잭션의 isolation level만 설정할 수 있다. (global 설정 불가능)
set transaction isolation level <isolation 수준>;
read uncommitted
set transaction isolation level read uncommitted;
MySQL과 다른점은 read uncommitted에서도 dirty read가 방지된다.
그래서 isolation level이 3개만 있다고 생각하라고 하네요. read uncommitted는 없는 겁니다.
read committed
set transaction isolation level read committed;
MySQL같이 Phantom Reads는 방어되지 못함.
repeatable read
set transaction isolation level repeatable read;
Non-repeatable Reads,Phantom Reads 방어된다.
그리고 MySQL과 차이점은 Serialization Anomaly도 방어된다. 커밋된 값을 변경하려하면 차단된다.
하지만 다른 방법으로 Serialization Anomaly가 나타난다.
Serialization Anomaly확인하는 방법 세션1에서 모든 계정을 조회하고 모든 값을 합한 계정을 만듭니다. 세션2에서도 같은 행동을 합니다.
둘다Commit하면 모든 값을 합한 계정이 2개 생깁니다. 근데 하나는 100이라면 다른 하나는 100을 포함한 값이 생겨야 하는데 그렇지 않고 둘 다 100으로 생깁니다. 이것이 Serialization Anomaly입니다.
Serializable
set transaction isolation level Serializable;
이렇게 하면 세션2에서 계정을 추가한 후 세션1 Commit까지는 성공적으로 오지만 세션2에서 Commit하려고 하면 읽기/쓰기 종속성으로 인한 액세스할 수 없다는 오류가 발생합니다.
그리고 다시 트랜잭션을 하라고 합니다. 이러면 중복 레코드를 생성하지 않게 됩니다.
다시 MySQL
MySQL에서는 잠금 메커니즘을 통해서 위의 상황을 해결해줍니다.
트랜잭션 1이 끝날때까지 다른 세션의 트랜잭션은 잠겨있습니다.
다른 방식으로 생성한다면 교착상태를 일으킵니다. 근데 교착상태를 일으켜서 다른 트랜잭션이 성공하도록 짜여져있습니다.
정리


MySQL :
Locking Mechanism 사용
Repeatable read가 기본
Postgres :
Dependencies Detection 사용
Read Committed가 기본
주의할 점은
error, timeout, deadlock으로 인한 이슈로 재시도 메커니즘을 잘 짜야한다.
db 엔진마다 isolation level 구현이 달라서 문서를 잘 읽어봐라.