<개요>

- Designing Data Intensive Applications 를 읽고 그 중 Transactions에 대한 내용을 정리.

- 이전글에서 Weak Isolation Levels 중 Read Commited, Snapshot Isolation(Repeatable Read)에 관련된 내용을 살펴보았고

 이번 글에서는 Preventing Lost Updates, Write Skew (Phantoms) 에 대해서 살펴본다.

- 이전글 https://icthuman.tistory.com/entry/Transactions-2

 

Transactions #2 (Atomicity, Isolation)

- Designing Data Intensive Applications 를 읽고 그 중 Transactions에 대한 내용을 정리. - 이전글 https://icthuman.tistory.com/entry/Transactions-개념정리-1 Transactions #1 (Basic) - Designing Data Intensive Applications 를 읽고 그

icthuman.tistory.com

 

<내용>

Weak Isolation Levels

 

Preventing Lost Updates

- read commited 와 snapshot isolation은 주로 동시 쓰기가 있는 경우, 읽기전용 트랜잭션의 볼수 있는 내용을 보장하는 것이다.

- 이전 글에서는 두 트랜잭션이 동시에 쓰는것에 대해서는 다루지 않았고, 특정 유형의 쓰기-쓰기 에 대해서만 살펴봤다.

- 몇 가지 다른유형의 충돌을 살펴 볼텐데 그 중 가장 유명한 것이 Lost Updates 이다.

- 아래 그림을 살펴보자.

TimeLine 1 2 3 4
User #1 getCounter 42 + 1 setCounter 43  
Data 42 42 43 43
User #2   getCounter 42 + 1 setCounter 43
 

- 두 Client간의 race condition이 발생하였다. 주로 App이 read - modify - write의 사이클을 가지고 있을때 발생한다.

- 즉 write가 발생하기 전에 그 사이에 일어난 modification을 포함하지 않기 때문이다.

 

Solutions

Atomic write Operations

- 많은 DB가 atomic update operations를 제공하기 때문에 App에서 해당 로직을 구현하지 않는 것이 좋다. 일반적인 Best Solution

 예) UPDATE counters SET val = val + 1 WHERE key = 'foo';

- 일반적으로 DB내부에서 execlusive lock을 통해서 구현하기 때문에 (update할때 읽을 수 없다!) "Cursor Stability 유지" 

- 다른 방법으로 all atomic operations를 single thread 에서 수행하는 방법도 있다. (성능 고려)

 

Explicit Locking

- DB에서 해당 기능을 제공하지 않는다면 App에서 명시적으로 update 될 object를 잠그는 방법이다. 

- 잠금을 수행후 read - modify - write 를 수행할 수 있으며, 다른 트랜잭션이 같은 object 에 접근할 경우에 첫 번째 read - modify - write 가 완료될 때 까지 강제로 대기한다.

예 )

BEGIN TRANSACTION

SELECT * FROM ... WHERE ... FOR UPDATE;  (해당쿼리로 반환된 all rows lock !)
UPDATE ... 

COMMIT;

 

Automatically detecting lost updates

- atomic operations & locks : read - modify - write 를 순차적으로 하여 lost updates 를 막는다.

- 대안 : 병렬 수행을 허락하고, Transaction Manager 가 lost update를 감지하면, 트랜잭션을 중단하고 강제로 read - modify - write 를 retry 한다!

- 장점 : DB가 이 검사를 효율적으로 수행할 수 있다는 것. with Snapshot Isolation

PostgreSQL repeatable read automatically detect
and abort the offending transaction
Orale serializable
SQL snapshot isolation
MySQL, InnoDB repetable read X

 

Compare-and-Set

- 우리가 CAS연산이라고 부르는 방법이다. DB가 Transcations를 제공하지 않는 atomic compare-and-set을 찾는 것이다. (Single-object writes)

- 마지막으로 값을 읽은 후 값이 변경되지 않았을때에만 업데이트가 발생할 수 있도록 허용하는 것이다.

- 만약 변경이 일어났다면? read-modify-write연산을 재시도한다. 반드시!

 

주의! Conflict replication

- Locks and Compared and Set은 Single up-to-date, copy of the data를 가정한다.

- replicated DB에서는 여러 노드에 복사본이 존재하고, 데이터 수정이 다른 노드에서 발생할 수 있기 때문에 다른차원의 접근이 필요하다.  즉, 다시 말하면 multi leader 또는 leaderless replication에서는 write가 동시에 발생하고, 비동기 연산이 있다면 보장할 수 없다. (Linearizability)

- 대신 "Detecting Concurrent Writes" 챕터에서 살펴본 내용처럼 concurrent writes 가 충돌된 값의 버전들을 생성하고 (App 또는 별도의 자료구조활용), 이러한 충돌을 versions를 통해서 reslove , merge하는 방법이 가능하다.

- Atomic Operations는 영향을 받지 않는다. (특히 Commutative한 Actions이라면 !)

- 슬프게도.. 많은 replicated DB에서는 기본값으로 Last Write Wins 이다.

 

<정리>

- 개인적으로 매우 유익했던 챕터이다. 결국 두 개 이상의 동시쓰기가 발생한다면 해결방법은 아래와 같이 정리할 수 있다.

 1) 해당 사이클을 통째로 묶는다.

 2) 동시수행을 제한한다. ( Lock or Single Thread )

 3) 일단 진행시켜! 에러나면 다시 시도

 

- 멀티 노드를 가지는 Database라면 여러 곳에서 동시다발적으로 데이터에 대한 복제 / 연산이 일어나기 때문에

 1) Single Leader를 통해서 제어하던지(Hbase 같은)

 2) 마지막에 Write한 값으로 저장

 3) 별도의 Application이나 자료구조를 활용하여 충돌버전을 관리하고 resolve / merge 

- 그래서 대부분의 분산병렬처리 오픈소스 진영에서 zookeeper를 사용하고 있는 듯 하다.

 

- 다음 글에서는 이 글에서 다루지 못한 Isolation Level ( Write Skew, Phantoms read )을 좀 더 자세히 살펴보고 분산환경의 Consistency 에 대해서 정리하도록 해야겠다.

+ Recent posts