<개요>

- 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 에 대해서 정리하도록 해야겠다.

<개요>

- Azure Cosmos 의 경우 RU 단위로 과금을 한다.

<내용>

- 400RU 가 최소단위이다.

- Partition Key를 잘 설정하지 않으면 HotSpot이 발생하여 RU만큼의 성능을 못 느낄 수도 있다.

- RU는 결국 초당 Read/Write를 위해서 접근하는 데이터의 양이라고 볼 수 있는데, 최적화를 잘못할 경우 기대한 한큼은 Throughput이 나오지 않을 수 있다.

- RU는 database 레벨에서 설정할 수도 있고 개별 Collection 단위로도 설정이 가능하다.

- 물리 1파티션당 10기가 용량제한이 있어서 이를 염두해야 한다.

 

<Scale설정화면>

Collection 내의 RU 설정화면

- 개별 Collection 의 사용빈도나 데이터가 다르다면 별도로 세팅하는 것이 좋다.

 

Database 내의 RU설정화면

 

database 레벨에서 RU를 설정할 경우 각 컨테이너의 RU는 별도설정이 불가능하고 database RU를 공유하는 방식이 된다.

컨테이너 RU 설정 공유화면

전체 사용량이 많지 않다면 공유하는 방법을 추천한다. (과금은 RU단위로 되기 때문)

<결론>

-400RU 로만 사용하면 월 5만원 안쪽으로 부담이 크지 않다.

-그러나 지역중복을 체크하면 x로 비용이 추가된다.

-RU를 초과하는 요청에 대해서는 즉시 응답이 오지 않으며 대기하다가 처리하는 방식으로 이루어진다.

-timeout이 발생하는 경우는 명시적으로 exception 이 발생한다.

-전체 data가 많을 경우 스캔해야하는 범위가 늘어나므로 RU최적화를 위해서는 data tts를 조정하는 것도 하나의 방법이 될 수 있다.

-파티션키 조정을 통해서 HotSpot발생을 줄이고 퍼포먼스를 증가시키는 것은 일반적인 NoSQL 과 유사하다.

+ Recent posts