Dirty Read
x = 10, y = 20
일 때 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 10 | |
write(y=70) | |
read(y) => 70 | |
write(x=80) | |
commit | |
rollback(y=20) |
그러면 최종적으로 x = 80, y = 20
이 되는데 이 때 x는 commit되지 않은 y를 read하여 얻은 결과이다. 이렇게 commit되지 않은 값을 읽는 것을 Dirty Read
라고 부른다.
Rollback이 일어나지 않은 다른 경우에도 Dirty Read
라고 부를 수 있는데 먼저 x = 50, y = 50
일 때 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 50 | |
write(x=10) | |
read(x) => 10 | |
read(y) => 50 | |
commit | |
read(y) => 50 | |
write(y=90) | |
commit |
이 경우는 transaction 2에서의 x + y = 60
이되고 transaction 1에서는 x + y = 100
이 되어 데이터 불일치가 발생한다고 볼 수 있다. 따라서 이러한 경우도 Dirty Read
라고 부르기도 한다.
Non-repeatable Read
x = 10
일 때 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 10 | |
read(x) => 10 | |
write(x=50) | |
commit | |
read(x) => 50 |
이 때 같은 데이터를 하나의 transaction 안에서 여러번 read 했을 때 값이 변경되는 현상이 일어난다. 그런데 Transaction의 Isolation 특성상 transaction끼리는 서로 영향을 주면 안되기 때문에 이상하다고 볼 수 있다. 그리고 이러한 현상을 Non-repeatable Read (Fuzzy Read)
라고 부른다.
Phantom Read
v라는 값을 가지는 tuple이 있을 때 v=10
인 tuple1과 v=50
인 tuple2에 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(v=10) => tuple1 | |
write(tuple2.v=10) | |
commit | |
read(v=10) => tuple1, tuple2 | |
commit |
이때도 Non-repeatable Read
와 비슷하게 다른 transaction에 의해 없던 데이터가 생기는 것을 Phantom Read
라고 부른다.
NOTE
Dirty Read
,Non-repeatable Read
,Phantom Read
와 같은 이상한 현상들이 모두 발생하지 않게 만들 수 있지만 제약사항이 많아져 동시 처리가능한 트랜잭션 수가 줄어들기 때문에 DB의 전체 throughput이 감소하게된다.
Isolation Level
- Isolation Level별 허용 여부
Isolation Level | Dirty Read | Non-reapeatable read | Phantom read |
---|---|---|---|
Read uncommitted | O | O | O |
Read committed | X | O | O |
Repeatable read | X | X | O |
Serializable | X | X | X |
NOTE
Serializable Level
은Dirty Read
,Non-repeatable Read
,Phantom Read
뿐만 아니라 이상한 현상 자체가 발생하지 않는 Level이다.
Dirty Write
x=10
에 대해 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
write(x=10) | |
write(x=100) | |
commit | |
abort |
transaction 2에서 100으로 수정했지만 transaction 1이 rollback을 하면서 잘못된 결과를 가져다주게 된다. 이렇게 commit이 안된 데이터를 write하는 현상을 Dirty Write
라고 부른다.
Lost Update
x=50
에 대해 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 50 | |
read(x) => 50 | |
write(x=200) | |
commit | |
write(x=100) | |
commit |
최종적으로 x=100
을 가지게되고 transaction 2에서 작업한 결과가 Lost된다. 이렇게 Update 작업이 사라지는 현상을 Lost Update
라고 부른다.
Read Skew
x=50, y=50
일 때 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 50 | |
read(x) => 50 | |
write(x=10) | |
read(y) => 50 | |
write(y=90) | |
commit | |
read(y) => 90 |
transaction 1에서의 x + y = 100
, transaction 2에서의 x + y = 140
이되어 데이터 불일치가 발생하게된다. 이렇게 일관성 없는 데이터를 읽는 현상을 Read Skew
라고 부른다.
NOTE
Read Scew는 Commit된 결과를 읽었을 때 발생하는 현상이고 Dirty Read는 Commit되지 않은 결과를 읽었을 때 발생하는 현상이다.
Write Skew
x=50, y=50
이고 x + y >= 0
이라는 제약사항이 있을 때 다음 작업을 수행한다고 해보자.
transaction 1 | transaction 2 |
---|---|
read(x) => 50 | |
read(y) => 50 | |
read(x) => 50 | |
read(y) => 50 | |
write(x=-30) | |
write(y=-40) | |
commit | |
commit |
transaction 1 작업과 transaction 2 작업에서는 각각 x + y >= 0
이라는 제약사항을 위반하지 않지만 두 transaction이 모두 수행된 결과는 제약사항을 위반하게 된다. 이러한 현상을 Write Skew
라고 부른다.
Snapshot Isolation
x=50, y=50
일 때 다음 작업을 수행한다고 해보자.
transaction 1 | snapshot 1 | transaction 2 | snapshot 2 |
---|---|---|---|
read(x) => 50 | x=50 | ||
write(x=10) | x=10 | ||
read(y) => 50 | y=50 | ||
write(y=150) | y=150 | ||
commit | |||
read(y) => 50 | x=10, y=50 | ||
write(y=90) | x=10, y=90 | ||
abort |
snapshot은 transaction이 시작될 때의 기준의 데이터를 의미한다. 따라서 transaction1의 read(y)
를 수행했을 때 transaction2에서 수행한 write(y=150)
이 반영된 데이터가 아닌 그 이전 값을 가져오는 것이다.
또한 transaction1에서 write(y=90)
이 적용된다면 Lost Update
현상이 일어나게 될 것이다. 하지만 Snapshot Isolation
에서는 처음 commit한 결과를 반영하고 이후에 수행하는 작업은 rollback처리를 한다.