profile image

L o a d i n g . . .

728x90

이전 포스팅에 쿼리 처리 비용을 계산해서 최적의 쿼리를 선택하는 방법에 대해 살펴봤습니다. 그리고 DBMS에서 동시에 실행되는 트랜잭션을 어떻게 처리하는지 살펴보겠습니다. 

Transaction Concurrency Control 

트랜잭션의 동시성 처리가 왜 중요할까요? 동시에 실행된 다수의 트랜잭션이 하나의 데이터를 수정할 때 어떻게 데이터의 일관성을 유지할 수 있을까요? 가장 단순한 방법은 한 시점에 하나의 트랜잭션만 데이터에 접근할 수 있도록 하는 방법입니다(예를 들면 데이터에 접근하는 트랜잭션을 순차적으로 실행하는 방식입니다). 하지만 트랜잭션을 순차적으로 실행하면 처리 속도가 줄고 DBMS의 응답 시간이 길어질 수 있습니다. 이를 해결하기 위해서는 데이터의 일관성을 유지하면서도 트랜잭션을 동시에 실행시킬 수 있는 방법이 필요합니다.   

Transaction Interleaving 

Transaction interleaving이란 다수의 트랜잭션이 동시에 실행될 때, 트랜잭션 내부의 여러 단계가 섞여(interleave) 실행되는 현상을 의미합니다. 다음 예시를 통해 transaction interleaving에 대해 살펴보겠습니다. 

Transaction Interleaving(Good)

A = 1000, B = 1000이라고 가정하겠습니다. 첫 번째 경우 두 트랜잭션의 최종적인 결과는 A = 954, B = 1166입니다. 두 번째 경우 두 트랜잭션의 최종 결과는 첫 번째와 동일합니다. 따라서 유효한 transaction interleaving으로 볼 수 있습니다.

Transaction interleaving(Bad)

왼쪽 트랜잭션 실행의 결과는 위에서 살펴봤기 때문에 오른쪽만 살펴보겠습니다. 오른쪽 트랜잭션 실행의 결과는 A = 954, B = 1160입니다. 최종 결과가 서로 다르기 때문에 유효하지 않은 transaction interleaving으로 볼 수 있습니다. 

그럼 어떤 경우를 유효한 transaction interleaving으로 볼 수 있을까요? 위에서 살펴봤듯이 유효한 transaction interleaving의 결과는 각 트랜잭션을 순차적으로 실행한 결과와 동일한 결과를 도출합니다. 

유효한 transaction interleaving의 결과는 각 트랜잭션을 순차적으로 실행한(serial execution) 결과와 동일합니다. 

Interleaved Execution Anomalies 

여러 트랜잭션이 하나의 자원에 동시에 접근하면 어떤 문제가 발생하는지 살펴보겠습니다. 

Read-Write Conflict 

Non-repeatable Read

두 개 이상의 트랜잭션이 동일한 데이터에 접근해서 읽기와 쓰기를 수행할 때 non-repeatable read 현상이 발생할 수 있습니다. 위의 예시에서 T1가 처음 A를 읽었을 때 결과는 10입니다. 하지만 T2가 A에 값 20을 쓰고 COMMIT을 수행하고 T1에서 다시 A를 읽으면 값은 20입니다. 하나의 트랜잭션에서 동일한 데이터를 여러 번 읽었을 때 서로 다른 결과를 반환하는 non-repeatable read 현상이 발생할 수 있습니다. 

Write-Read Conflict 

두 트랜잭션이 쓰기와 읽기를 수행했을 때 dirty read 현상이 발생할 수 있습니다.

Dirty Read

T1에서 A에 20이라는 값을 쓰고 COMMIT을 수행하기 전에 T2에서 A를 읽어서 특정 연산을 수행 후 30이라는 값을 쓰고 COMMIT을 수행합니다. 하지만 최종적으로 T1에서 ABORT를 통해 트랜잭션을 롤백하면 T2에서 읽었던 A = 20이라는 값은 유효하지 않은 값이 됩니다. 이와 같이 COMMIT 되지 않은 트랜잭션의 결과로 생성된 값을 읽는 현상을 dirty read라고 합니다. 

Write-Write Conflict 

Overwriting Uncommitted Data

두 트랜잭션이 동일 데이터에 동시 쓰기를 수행을 하면 기존 데이터를 덮어 씌우는 현상이 발생합니다. 위의 예시에서 T1은 A에 값 10을 쓰고 T2에서 중간에 A에 값 20을 씁니다. 이와 같이 두 개 이상의 트랜잭션에서 동시에 쓰기를 수행하면 덮어쓰기 현상이 발생할 수 있습니다. 

Schedule Serializability 

위에서 트랜잭션이 동시에 실행되면 어떤 문제가 발생하는지 살펴봤습니다. 여러 트랜잭션이 동시에 실행된 결과트랜잭션을 순차적으로 실행한 결과와 동일할 때 해당 트랜잭션들을 직렬화 가능하다고(serializable) 볼 수 있습니다. 이 트랜잭션들을 실행시키는 실행 계획을 schedule이라고 합니다. Schedule은 직렬화의 정도를 기준으로 conflict serializable schedule과 view serializable schedule로 구분됩니다. 

Conflict Serializable Schedule

Schedule이 conflict serializable(conflict는 위에서 살펴본 read-write, write-read, write-write conflict를 의미합니다) 하기 위해서는 해당 schedule에서 실행되는 트랜잭션 간 발생하는 conflicting action트랜잭션이 순차 실행(직렬화)되는 schedule에서 발생하는 conflicting action과 동일해야 합니다. Schedule이 conflict serializable 하다면 트랜잭션 간 non-conflicting 작업의 순서를 바꿀 수 있습니다.

출처:https://15445.courses.cs.cmu.edu/fall2021/slides/15-concurrencycontrol.pdf

위의 예시에서 R(B) <-> W(A), R(B) <-> R(A), W(B) <-> W(A), W(B) <-> R(A)는 서로 다른 데이터에 접근하기 때문에 non-conflicting 작업입니다. 따라서 실행하는 순서를 바꿀 수 있습니다.

출처: https://15445.courses.cs.cmu.edu/fall2021/slides/15-concurrencycontrol.pdf

위의 예시는 non-conflict serializable schedule 대한 예시입니다. 기존 schedule과 트랜잭션을 직렬화(serialize)했을 때 발생하는 conflicting action이 서로 다르기 때문입니다. 

Conflict serializable scheduling 방식은 non-conflicting 작업의 순서를 변경하는 방식으로 conflict serializability를 판단합니다. 2개의 트랜잭션만 존재하는 경우 유용할 수 있지만 트랜잭션의 수가 많을 때 비효율적일 수 있습니다. 효율적으로 conflict serializability를 판단하기 위해 precedence graph를 활용할 수 있습니다. 

Precedence Graph

트랜잭션 Ti에 포함된 작업 Oi가 트랜잭션 Tj에 포함된 작업 Oj보다 먼저 실행되고, Oi와 Oj 작업을 수행했을 때 conflict가 발생한다면 precedence graph는 위와 같습니다. 여러 트랜잭션을 스케쥴링할 때 precedence graph를 그려봄으로써 conflict serializable 여부를 파악할 수 있습니다. 만약 precedence graph에서 cycle이 발생하지 않는다면 conflict serializable 하다고 볼 수 있습니다. 

 

출처: https://15445.courses.cs.cmu.edu/fall2021/slides/15-concurrencycontrol.pdf

위의 예시에서 precedence graph는 cycle이 발생합니다. 따라서 해당 스케줄은 conflict serializable 하지 않습니다. 

출처: https://15445.courses.cs.cmu.edu/fall2021/slides/15-concurrencycontrol.pdf

위의 예시는 cycle이 발생하지 않기 때문에 conflict serializable 한 스케줄로 볼 수 있습니다. 

마무리 

이번 포스팅을 통해 어떻게 트랜잭션 간 동시성 문제를 해결하는지 살펴봤습니다. 다음 포스팅에서는 2 phase locking에 대해 살펴보겠습니다. 

728x90
복사했습니다!