DBMS - Deadlock: 이해와 데이터베이스 병목 현상 방지

안녕하세요, 데이터베이스 열정가 여러분! 데이터베이스 관리 시스템(DBMS)과 그 중 가장 복잡한 개념 중 하나인 데드락에 대해 안내해드리게 되어 매우 기쁩니다. 프로그래밍에 처음 도전하는 분이라도 걱정하지 마세요; 기본부터 차근차근 설명해드릴 테니까요. 이 튜토리얼을 마치시면 데드락 탐정이 되어 이러한 귀찮은 데이터베이스 병목 현상을 찾아내고 방지할 수 있을 것입니다!

DBMS - Deadlock

데드락이란?

그림을 상상해보세요. 여러분과 친구가 한 자리에 앉아 단 한 개의 포크와 칼만이 있는 테이블에 앉아 있습니다. 여러분은 포크를 들고 있고, 친구는 칼을 들고 있으며, 서로 다른 쪽의 도구를 얻지 않는 이상 놓지 않으려고 합니다. 이것이 데이터베이스 세계에서 데드락이란 것입니다!

DBMS 용어로는, 데드락은 두 개 이상의 트랜잭션이 서로가 가지고 있는 자원을 해제할 때까지 기다리는 상황을 의미합니다. 디지털적인 멕시코 서 divan stand-off처럼, 아무도 전진할 수 없습니다.

간단한 예를 보겠습니다:

-- 트랜잭션 1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- 트랜잭션 2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 2;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 1;
COMMIT;

이 시나리오에서, 트랜잭션 1이 계정 1을 업데이트하고 트랜잭션 2가 계정 2를 업데이트하는 동안, 서로가 무한정 기다릴 수 있습니다. 이는 데드락이 발생하는 것입니다.

데드락 방지

이제 데드락이 무엇인지 이해했으니, 그 방지 방법을 탐구해보겠습니다. 데드락 방지는 시스템을 구성하여 데드락이 발생할 수 없도록 하는 것입니다.

1. 자원 순서

효과적인 방법 중 하나는 항상 특정 순서로 자원을 요청하는 것입니다. 식사하는 사람들에게 "먼저 포크를 들고, 그 다음 칼을 들어"라고 말하는 것과 같습니다. 이렇게 하면 서로 다른 쪽의 필요한 도구를 들고 있는 상황이 발생하지 않습니다.

이전 예제를 데드락을 방지하도록 다시 작성해보겠습니다:

-- 트랜잭션 1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- 트랜잭션 2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 2;
COMMIT;

계정 1을 먼저 업데이트하고 계정 2를 업데이트함으로써 트랜잭션이 데드락이 되지 않도록 합니다.

2. 락 타임아웃

락 타임아웃을 사용하는 또 다른 방지 방법은 있습니다. 식사하는 사람들에게 "5분 내에 두 가지 도구를 얻지 못하면 포기하고 나중에 다시 시도하라"고 말하는 것과 같습니다.

SQL Server에서 락 타임아웃을 설정하는 방법:

SET LOCK_TIMEOUT 5000; -- 타임아웃을 5초로 설정

이렇게 하면 트랜잭션이 5초 내에 락을 획득할 수 없으면 자동으로 롤백되어 데드락을 방지합니다.

3. 락 기간 단축

자원이 락된 시간이 짧을수록 데드락이 발생할 가능성이 낮습니다. 식사하는 사람들에게 " 빠르게 먹어라!"라고 말하는 것과 같습니다. 데이터베이스 용어로는 트랜잭션을 최대한 짧게 유지하는 것입니다.

락 기간을 줄이는 예제:

-- 이렇게 하지 말고:
BEGIN TRANSACTION;
-- 긴 처리 작업을 수행
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

-- 이렇게 하세요:
-- 긴 처리 작업을 수행
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

긴 처리 작업을 트랜잭션 외부에서 수행하여 락이 유지되는 시간을 줄입니다.

데드락 회피

방지는 데드락이 발생할 수 없도록 하는 것이라면, 회피는 실행 시간에 지능적인 결정을 내려 잠재적인 데드락을 피하는 것입니다.

1. 대기-죽음(Wait-Die) 방법

이 방법에서는 더 오래된 트랜잭션이 우선됩니다. 더年轻的 트랜잭션이 오래된 트랜잭션이 가지고 있는 자원을 요청하면 "죽음"을 당합니다(롤백하고 다시 시작합니다). 오래된 트랜잭션이 더年轻的 트랜잭션이 가지고 있는 자원을 요청하면 기다립니다.

이를 표현한 퓨토코드:

def request_resource(transaction, resource):
if resource.is_held_by_younger_transaction(transaction):
wait(transaction, resource)
else:
die_and_restart(transaction)

2. 상처-대기(Wound-Wait) 방법

이는 Wait-Die와 반대입니다. 오래된 트랜잭션이 더年轻的 트랜잭션을 "상처"를 입혀(롤백시킨다) 더年轻的 트랜잭션이 오래된 트랜잭션을 기다립니다.

def request_resource(transaction, resource):
if resource.is_held_by_younger_transaction(transaction):
wound(resource.holder)
else:
wait(transaction, resource)

3. 은행가 알고리즘

이 지능적인 알고리즘은 자원 요청이 데드락을 초래할 수 있는지 결정합니다. 만약 그렇다면 요청을 거부합니다.

은행가 알고리즘의 간단한 버전:

def is_safe_state(available, max_need, allocation):
work = available.copy()
finish = [False] * len(allocation)

while True:
found = False
for i in range(len(allocation)):
if not finish[i] and (max_need[i] - allocation[i] <= work).all():
work += allocation[i]
finish[i] = True
found = True

if not found:
break

return all(finish)

def request_resources(process_id, request):
if request > max_need[process_id] - allocation[process_id]:
return False  # 요청이 최대 주장을 초과합니다

if request > available:
return False  # 자원이 없습니다

# 임시로 자원을 할당합니다
available -= request
allocation[process_id] += request

if is_safe_state(available, max_need, allocation):
return True  # 요청을 승인합니다
else:
# 변경 사항을 되돌리고 요청을 거부합니다
available += request
allocation[process_id] -= request
return False

이 알고리즘은 요청을 승인하면 시스템이 안전한 상태를 유지하는지 확인합니다. 그렇지 않으면 요청을 거부합니다.

결론

축하합니다! 데드락 방지와 회피의 세계로 첫 걸음을 내셨습니다. 데드락을 처리하는 것은 바쁜 교차로에서 교통 경찰이 되는 것과 같습니다. 때로는 문제가 발생하기 전에 방지하는 것이 필요하며(교통 신호를 설정하는 것처럼), 때로는 사고를 피하기 위해 빠른 결정을 내리는 것이 필요합니다(수동으로 교통을 조정하는 것처럼).

데이터베이스 관리의 여정을 계속하면서 더 복잡한 시나리오를 만나게 될 것입니다. 하지만 여기서 다루었던 원칙들은 견고한 기반을 제공할 것입니다. 계속 연습하고, 호기심을 유지하며, 실험을 두려워하지 마세요. 모든 위대한 데이터베이스 관리자는 여러분이 현재 있는 자리에서 시작했습니다!

행복한 코딩을 기원하며, 여러분의 트랜잭션이 항상 데드락이 되지 않기를 바랍니다!

방법 카테고리 설명
자원 순서 방지 항상 특정 순서로 자원을 요청
락 타임아웃 방지 트랜잭션이 락을 획득하는 데 최대 시간을 설정
락 기간 단축 방지 트랜잭션을 최대한 짧게 유지
대기-죽음 방법 회피 오래된 트랜잭션이 기다리고,年轻的 트랜잭션이 롤백
상처-대기 방법 회피 오래된 트랜잭션이年轻的 트랜잭션을 롤백하고 기다림
은행가 알고리즘 회피 자원 요청이 데드락을 초래할 수 있는지 결정

Credits: Image by storyset