Web/DataBase

[ Database - Intermediate ] - Isolation level

Hyunseo😊 2023. 7. 18. 18:01

위와같은 상황을 다시 한번 리마인드 해보겠습니다. 흐름도를 보면, x=10, y=20에서 시작해서 x는 write(y=70)된 다음 y값을 읽어서 80으로 갱신한다음에 commit했는데, tx2는 abort가 되어 x의 값이 좀 이상해지게 되는데 즉 이를 Dirty read(commit 되지 않은 변화를 읽음) 입니다.

 

그리고 만약 tx1이 x를 두번 읽는데, 같은 데이터의 값을 달라지는 아래와 같은 상황을 보겠습니다.

이는 isolation 관점에서 봤을 때. isolation은 마치 트랜잭션이 고립된 환경에서 실행되어야 하는데, x를 읽었더니 값이 달라지게 되면 이는 좀 이상합니다. 그리고 우리는 이를 Non-repeatable read, Fuzzy read라고 합니다.

 

그리고 위와같이 없던 데이터가 갑자기 생기는 상황을 Phantom read라고 합니다.

 

위 3개의 현상들은 모두 발생하지 않게끔 해주는 것이 좋습니다. 이런 이상한 현상들이 모두 발생하지 않게 만들 수는 있지만, 그러면 제약사항이 만들어져서 동시 처리 가능한 트랜잭션 수가 줄어들어 결국 DB의 전체 처리량(throughput)이 하락하게 됩니다. 그래서 우리는 일부 이상한 현상은 허용하는 몇 가지 level을 만들어서 사용자가 필요에 따라서 적절하게 선택할 수 있도록 하자! 라는 아이디어가 isolation level인 것입니다. 이를 표로 정리한 것을 보겠습니다.

 

위와같이 허용하는 이상한 현상들에 따라서 isolation level이 read uncommited, read commited, repeatable read, serializable로 나뉘게 됩니다. 즉 애플리케이션 설계자는 isolation level을 통해 전체 처리량(throughput)과 데이터 일관성 사이에서 어느정도 거래(trade)를 할 수 있다는 뜻이 됩니다.

 

그리고 Dirty read와 비슷하게 commit되지 않은 wirte 하는 것을 Dirty write라고 합니다. 그래서 rollback 시에 정상적인 recovery는 매우 중요하기 때문에 모든 isolation level에서 dirty write를 허용하면 안된다고 논문에서 얘기합니다. 

 

뿐만 아니라 중간에 commit 된 정보가 없어지는걸 이전에도 쭉 얘기해왔지만 Lost update하고 합니다. 이는 Update를 덮어쓸 때 발생합니다. 그리고 추가적으로 Dirty read에서 위에서 언급하지 않은 경우에도 발생한다고 되어 있는대 아래의 그림을 보겠습니다.

 

위의 상황을 다시 보면, 정상적으로 잘 동작한 거 같지만, tx2가 read(x)의 결과로 10을 read(y)의 결과로 50을 읽어오는데, x+y = 100이 아니여서 데이터 비일관성이 발생하게 됩니다. 이처럼 tx2에서 abort가 발생하지 않아도 dirty read가 발생할 수 있습니다. 또 다른 상황을 보겠습니다.

 

위 상황에서는 tx2에서 read(x) = 50, read(y) = 90이 됩니다. 이는 tx1에서 업데이트를 해주었기 때문인데요 이를 Read skew라고 합니다. 이는 위에서 본 Repeatable read와 비슷한데, tx2가 x를 2번 읽으면 동일한 상황이 됩니다. 이의 원인은 당연히 inconsistent한 데이터를 읽었기 때문입니다. 좀 더 상황을 보겠습니다.

 

위와같은 상황에서 추가적으로 x + y >= 0이라는 제약사항이 추가되었다고 합시다. 위 상황에서는 최종 결과가 이 제약사항을 만족하지 않고 있습니다. 원래 serial하게 진행되었다면 tx2의 write가 되지 않고 abort되어야 했을 겁니다. 이러한 inconsistent한 데이터 쓰기를 Write skew라고 합니다. 마지막으로 다른 예시를 보겠습니다.

 

위와같이 이전에 Phantom read라고 해서 없던 데이터가 추가된 상황을 볼 수 있었습니다. 하지만 위 경우도 어떻게 보면 Phantom read라고 볼 수 있는데, read(v > 10)이 분명 없었는데, tx2에서 write(v2 = 15)를 하고 commit하면서 tx1에 read(cnt)를 할 때 갑자기 1이 되는 기이한 현상이 발생합니다. 이 또한 우리는 확장적 의미의 Phantom read라고 볼 수 있을 것입니다. 

 

SNAPSHOT ISOLATION

우리는 dirty read, repeatable read, phantom read와 같이 3가지 대표적인 기이한 현상과 확장적 의미의 이상한 현상을 살펴보았습니다. 이는 모두 standard SQL 92라는 표준을 비판한 논문에 있는 내용입니다. 여기에서는 추가적으로 SNAPSHOT ISOLATION을 말하고 있습니다.  이는 매우 중요한 내용입니다.

 

기존에 isolation level은 기이한 현상을 먼저 정의하고 이에 대한 허용 범위를 설정한다음에 레벨을 설정했었습니다. 하지만 SNAPSHOT ISOLATION은 isolation을 "어떻게" 구현할 것인가에 따라서 level을 나눈 것이라고 보면 됩니다. 간단한 예제를 보겠습니다.

 

SNAPSHOT ISOLATION은 transaction이 시작한 시점에서의 스냅션을 통해서 약간 1차 캐시의 느낌쓰를 통해 DB의 commit / rollback을 관리하는 것입니다. 예를 들어 위 상황에서 read(x) 의 위 시점에서 tx1을 위한 snapshot을 찍고, read(y)의 시점에서 tx2에 대한 snapshot을 각각 찍습니다. 그리고 tx2가 commit되면 snapshot에 y=150으로 되어있을 텐데, 이를 DB에 FLUSH하면서 반영합니다. 그리고 그 다음에 read(y)를 tx1에서 진행하는데, 이 값은 150이 아닙니다. 그 이유는 tx1은 자신이 처음에 찍은 snapshot을 참조하기 때문에 50이라는 값을 읽고 40을 추가해서 y에 90을 snapshot에 적용합니다. 하지만 이 y값을 DB에 적용해주면, 이는 Lost update(write-write conflict 때문)가 발생하는 꼴이 되어버립니다. 따라서 이는 abort됩니다.

 

SNAPSHOT ISOLATION은 즉, 먼저 commit한 놈이 실제 DB에 반영되는 겁니다. 그리고 이는 MVCC(multi version concurrency control)의 한 버전입니다. 

 

SQL 표준에서 정의한 isolation level

실제 실무에서 사용하는 RDBMS에서는 isolation level을 어떻게 적용하고 있을까요?

 

MySQL (innoDB)같은 경우에는, isolation level을 표준에서 정의한 isolation level과 동일하게 적용하고 있습니다. 위와 동일하다는 것이죠. 

 

Oracle또한 표준과 동일한 isolation level을 제공하고 있지만, 이는 Read uncommitted는 제공하지 않습니다. 그리고 Repeatable read, serializable을 적용하려면 트랜잭션 isolation level을 모두 serializable로 적용하라고 가이드되어 있습니다. 즉 Oracle에서는 read committed, serializable이 주를 이룬다고 보면 됩니다.

 

SQL server는 위 표준과 Snapshot isolation level을 추가하고 대표 3가지 기이한 현상을 기반으로 적용됩니다.

 

PostgreSQL 또한 표준 isolation level을 따르는데, 3가지 현상을 포함해서 Serialization Anomaly를 추가적으로 들어서 기이한 현상을 나눕니다. 

 

주요 RDBMS는 SQL 표준에 기반해서 isolation level을 정의한다는 것을 알 수 있고, RDBMS마다 제공하는 isolation level도 다르다는 것을 알 수 있습니다. 또한 같은 이름의 isolation level이더라도 동작 방식이 다를 수 있습니다.

 

즉 위 3가지 보다는, 다양한 현상들을 모두 파악하려고 노력해야 합니다!