데이터베이스 관리 시스템(DBMS) - 동시성 제어: 초보자 가이드
안녕하세요, 미래의 데이터베이스 마법사 여러분! 오늘 우리는 데이터베이스 관리 시스템(DBMS)의 동시성 제어 세계로 흥미로운 여정을 떠납니다. 이 주제에 대해 새로운 사람이라면 걱정하지 마세요; 저는 여러분의 친절한 안내자가 되겠습니다. 이 주제를 단계별로 탐구해 보겠습니다. 그럼 커피 한 잔을 손에 들고, 함께 빠져들어 보겠습니다!
동시성 제어는 무엇인가요?
자, 구체적인 내용으로 들어가기 전에 동시성 제어가 무엇인지 이해해 보겠습니다. 바쁜 식당에서 여러 명의 웨이터가 동시에 주문을 받고 음식을 서빙하는 상황을 상상해 보세요. 적절한 協조가 이루어지지 않으면 혼란이 빚어질 것입니다! 마찬가지로, 데이터베이스에서 여러 사용자나 프로세스가 동시에 데이터에 접근하고 수정하려고 시도할 수 있습니다. 동시성 제어는 그러한 충돌을 방지하고 모든 것이 원활하게 작동하도록 보장하는 '헤드 웨이터'와 같은 역할을 합니다.
이제 DBMS에서 사용되는 주요 동시성 제어 기술을 탐구해 보겠습니다.
락 기반 프로토콜
락 이해하기
락은 호텔 방 문에 달린 "방해 금지" 표지와 같습니다. 트랜잭션이 데이터에 접근해야 할 때, 그 데이터에 락을 걸어 다른 사람들에게 "여기서 일하고 있어!"라고 알립니다.
락 종류
락 종류 | 설명 | 사용 사례 |
---|---|---|
공유 락 (S) | 여러 트랜잭션이 데이터를 읽을 수 있음 | 데이터를 수정하지 않고 읽는 경우 |
독점 락 (X) | 오직 하나의 트랜잭션이 이 락을 소유할 수 있음 | 데이터를 쓰거나 업데이트하는 경우 |
이중단계 락(2PL) 프로토콜
이 프로토콜은 두 가지 주요 동작을 가진 춤과 같습니다:
- 성장 단계: 락을 획득하고, 어떤 락도 해제하지 않음.
- 수축 단계: 락을 해제하고, 새로운 락을 획득하지 않음.
간단한 예를 보겠습니다:
BEGIN TRANSACTION;
-- 성장 단계
LOCK TABLE users IN EXCLUSIVE MODE;
UPDATE users SET balance = balance - 100 WHERE id = 1;
LOCK TABLE transactions IN EXCLUSIVE MODE;
INSERT INTO transactions (user_id, amount) VALUES (1, -100);
-- 수축 단계
UNLOCK TABLE users;
UNLOCK TABLE transactions;
COMMIT;
이 예제에서, 우리는 먼저 필요한 테이블을 락하고, 작업을 수행한 후 락을 해제하여 트랜잭션을 커밋합니다.
데드락: 춤이 잘못된 경우
두 명의 춤 추는 사람이 서로의 동작을 기다리는 상황을 상상해 보세요. 그것은 데드락입니다! 데이터베이스에서는 두 트랜잭션이 서로를 기다리는 상황이 발생할 수 있습니다.
데드락을 방지하기 위해 다음과 같은 기술을 사용합니다:
- 타임아웃: 트랜잭션이 너무 오래 기다리면 롤백합니다.
- 데드락 탐지: 시스템이 데드락을 적극적으로 탐지하고 해결합니다.
타임스탬프 기반 프로토콜
이제 타임스탬프 기반 프로토콜에 대해 이야기해 보겠습니다. 이는 각 트랜잭션이 시스템에 진입할 때 고유한 타임스탬프 티켓을 부여하는 것과 같습니다.
기본 타임스탬프 순서(TO) 프로토콜
이 프로토콜에서는 타임스탬프를 사용하여 충돌하는 연산의 순서를 결정합니다. 이는 고객이 식당에 도착한 시간에 따라 서비스를 제공하는 것과 같습니다.
다음은 그 작동 방식입니다:
- 각 데이터 항목 X는 두 가지 타임스탬프 값을 가집니다:
- W-timestamp(X): X를 성공적으로 기록한 트랜잭션 중 가장 큰 타임스탬프.
- R-timestamp(X): X를 성공적으로 읽은 트랜잭션 중 가장 큰 타임스탬프.
- 트랜잭션 T가 X를 읽으려고 할 때:
- 만약 TS(T) < W-timestamp(X)이면, T는 너무 늦고 롤백해야 합니다.
- 그렇지 않으면, T가 X를 읽고 R-timestamp(X)를 max(R-timestamp(X), TS(T))로 설정합니다.
- 트랜잭션 T가 X를 쓰려고 할 때:
- TS(T) < R-timestamp(X) 또는 TS(T) < W-timestamp(X)이면, T는 너무 늦고 롤백해야 합니다.
- 그렇지 않으면, T가 X를 쓰고 W-timestamp(X)를 TS(T)로 설정합니다.
예제를 보겠습니다:
class DataItem:
def __init__(self):
self.value = None
self.r_timestamp = 0
self.w_timestamp = 0
def read(transaction, data_item):
if transaction.timestamp < data_item.w_timestamp:
print(f"트랜잭션 {transaction.id}는 읽기에 너무 늦었습니다. 롤백합니다...")
abort(transaction)
else:
print(f"트랜잭션 {transaction.id}는 값 {data_item.value}를 읽습니다.")
data_item.r_timestamp = max(data_item.r_timestamp, transaction.timestamp)
def write(transaction, data_item, new_value):
if (transaction.timestamp < data_item.r_timestamp or
transaction.timestamp < data_item.w_timestamp):
print(f"트랜잭션 {transaction.id}는 쓰기에 너무 늦었습니다. 롤백합니다...")
abort(transaction)
else:
print(f"트랜잭션 {transaction.id}는 값 {new_value}를 씁니다.")
data_item.value = new_value
data_item.w_timestamp = transaction.timestamp
def abort(transaction):
print(f"트랜잭션 {transaction.id}는 롤백되고 다시 시작됩니다.")
이 예제에서, 우리는 타임스탬프 순서 프로토콜을 따르는 기본적인 읽기와 쓰기 연산을 구현했습니다. 시스템은 연산을 허용하기 전에 타임스탬프를 확인하고 적절히 업데이트합니다.
Thomas Write Rule: 지혜로운 최적화
Thomas Write Rule은 빠른 달리기 선수가 느린 선수를 지나가는 것과 같습니다. 이는 일부 "너무 늦은" 쓰기 연산을 무시할 수 있도록 허용하여 트랜잭션 롤백을 줄입니다.
다음은 그 작동 방식입니다:
만약 TS(T) < W-timestamp(X)이면, T를 롤백하지 않고 이 쓰기 연산을 무시합니다. 이는 기록되는 값이 이미 오래된 값이기 때문에 안전합니다.
우리의 write 함수를 Thomas Write Rule을 포함하여 수정해 보겠습니다:
def write_with_thomas_rule(transaction, data_item, new_value):
if transaction.timestamp < data_item.r_timestamp:
print(f"트랜잭션 {transaction.id}는 쓰기에 너무 늦었습니다. 롤백합니다...")
abort(transaction)
elif transaction.timestamp < data_item.w_timestamp:
print(f"트랜잭션 {transaction.id}의 쓰기는 Thomas Write Rule로 무시됩니다.")
else:
print(f"트랜잭션 {transaction.id}는 값 {new_value}를 씁니다.")
data_item.value = new_value
data_item.w_timestamp = transaction.timestamp
이 최적화는 불필요한 트랜잭션 롤백을 줄여 전체 시스템 성능을 향상시킵니다.
결론
와우! 오늘 우리는 많은 내용을 다루었습니다. 락 기반 프로토콜에서 타임스탬프 기반 프로토콜까지. 동시성 제어는 동시 데이터베이스 연산의 혼란을 방지하고 순서를 유지하는 것입니다. 이는 교차로에서 교통 경찰이 모든 차량이 부딪히지 않고 목적지에 도달하도록 보장하는 것과 같습니다.
데이터베이스 세계에서 계속 여정을 이어가면서 더 발전된 개념과 기술을 만나게 될 것입니다. 하지만 지금은 이 기본적인 동시성 제어 개념을 마스터한 것을 자랑해 보세요!
계속 연습하고, 호기심을 유지하고, 행복하게 코딩하세요!
Credits: Image by storyset