개발 블로그

[Database] DB Lock의 종류와 DeadLock 본문

데이터/데이터베이스

[Database] DB Lock의 종류와 DeadLock

갹둥 2024. 8. 19. 01:11

DB Lock

  • 여러 트랜잭션이 동시에 동일한 데이터를 액세스할 때 데이터 무결성을 유지하고 충돌을 방지하는 매커니즘
  • 종류는 아래와 같습니다
    • LS-Shared Lock : 공유락
    • LX-Exclusive Lock: 배타락

 

1. Lock의 종류

공유 락(LS-Shared Lock)

  • 여러 트랜잭션이 동시에 동일한 리소스를 읽을 수 있도록 허용
  • 공유 락이 걸린 데이터는 읽기 전용임, 변경 불가
  • 데이터 일관성을 유지하기 위해 주로 사용, SELECT 쿼리에서 사용(락 걸리니깐 무작정 사용하지는 말기...)
--Transaction 1: 공유 락 설정
SELECT * FROM member WHERE last_name = 'Kim' LOCK IN SHARE MODE;

--Transaction 2: 공유 락 설정
SELECT * FROM member WHERE last_name = 'Lee' LOCK IN SHARE MODE;
  • 두 트랜잭션에서 같은 리소스에 동시에 접근 가능

 

 

배타 락(LX-Exclusive Lock)

  • 특정 리소스에 대해 한 트랜잭션이 배타적으로 액세스하고 변경할 수 있도록 허용
  • 배타 락이 걸리 데이터는 다른 트랜잭션이 읽거나 쓸 수 없음
  • 데이터 수정시 무결성을 보장하기 위해 사용하며 주로 UPDATE, DELETE 쿼리에서 사용
-- Transaction 1: 배타 락 설정
START TRANSACTION;
UPDATE member SET first_name = 'Ga Eun' WHERE first_name = 'gagle';

-- Transaction 2: 동일 리소스에 대해 배타 락 시도(대기 상태)
START TRANSACTION;
UPDATE member SET first_name = '가은' WHERE first_name = 'gagle';

 

  • FOR UPDATE을 사용한 SELCT문은 배타 락으로 설정됨
SELECT * FROM member WHERE last_name = 'Kim' FOR UPDATE;

 

 

 

2. 데드락(교착상태)

  • 두 개 이상의 트랜잭션이 서로의 락을 기다리며 무한 대기 상태에 빠지는 상황
  • 이는 시스템의 성능을 저하시킬 뿐만 아니라 트랜잭션 처리에 치명적인 영향을 미칠 수 있음

 

1. 교착상태의 발생 조건

  • 교착상태에 빠지려면 아래 발생조건 4가지를 모두 충족해야함
  1. 상호 배제(Mutual Exclusion): 자원은 한 번에 하나의 트랜잭션만 사용할 수 있음. 트랜잭션 A가 특정 자원을 락으로 점유하고 있는 동안, 트랜잭션 B는 그 자원을 사용할 수 없음
  2. 점유 및 대기(Hold and Wait): 트랜잭션이 적어도 하나의 자원을 점유하고 있으면서, 추가로 다른 자원의 사용을 요청하며 대기 상태에 있음. 예를 들어, 트랜잭션 A가 자원 X를 락하고 자원 Y를 요청할 때, 자원 Y가 트랜잭션 B에 의해 락되어 있으면 트랜잭션 A는 자원 Y를 기다림
  3. 비선점(Non-preemption): 트랜잭션이 점유한 자원을 강제로 빼앗을 수 없음
  4. 순환 대기(Circular Wait): 두 개 이상의 트랜잭션이 원형(사이클)으로 자원을 서로 점유하고 있으며, 각 트랜잭션은 다음 트랜잭션이 점유한 자원을 기다리고 있음. 예를 들어, 트랜잭션 A가 자원 X를 점유하고 자원 Y를 기다리며, 트랜잭션 B가 자원 Y를 점유하고 자원 X를 기다리는 경우

 

2. 예제

MySQL 로컬 db로 간단한 데드락 상황을 만들어봤습니다

원래 테이블

 

첫번 째 세션에서 트랜잭션을 시작하고 1번 row에 update를 해줍니다(선점 시작, id = 1 인 행에 대한 배타 락 획득)

 

두번 째 세션에서 트랜잭션을 시작하고 2번 row에 update를 해줍니다(선점 시작, id = 12 인 행에 대한 배타 락을 획득)
첫번 째 트랜잭션에서 2번 row에 접근합니다(id = 2인 행에 대한 배타 락 요청, 대기 시작)
두 번째 트랜잭션에서 1번 행에 접근합니다 ( id = 1인 행에 대한 배타 락 요청, 대기 시작)

  • 세션 1은 세션 2가 점유하고 있는 id = 2의 자원을 기다리고, 세션 2는 세션 1이 점유하고 있는 id = 1의 자원을 기다림
  • 이렇게 서로 자원을 기다리는 상황이 발생하면 데드락이 발생
  • 에러메세지가 반환되고 세션 2가 자동 롤백되었음

 

 

3. 데드락의 탐지

  • 대부분의 데이터베이스 시스템은 주기적으로 데드락을 감지하기 위한 메커니즘을 가지고 있음
  1. Wait-For 그래프(Wait-For Graph): 데이터베이스의 트랜잭션과 자원 간의 의존성을 그래프로 표현, 각 트랜잭션을 노드로, 자원을 기다리는 상태를 간선으로 표시합니다. 그래프에 사이클(순환)이 존재하면 데드락이 발생한 것

  2. 타임아웃 기반 탐지: 트랜잭션이 특정 시간 동안 자원을 획득하지 못하면 데드락이 발생했다고 판단하고 해당 트랜잭션을 강제 종료, 타임아웃 설정이 적절하지 않으면 정상적인 트랜잭션이 중단될 위험이 있음

 

 

4. 데드락 해결 방법

  • 트랜잭션 강제 종료
    • 데드락이 감지되면, 관련된 트랜잭션 중 하나 또는 모두를 강제로 종료(rollback)하여 자원을 해제
    • 가장 적게 작업한 트랜잭션을 종료하거나, 우선순위가 낮은 트랜잭션을 종료하는 방법이 일반적
  • 재시도
    • 강제 종료된 트랜잭션을 일정 시간 후 다시 시도하게 하여 데드락 상황을 해결
    • 트랜잭션이 충돌하지 않을 가능성을 높이기 위해 적용됨

 

 

3. 데드락 처리 전략

사실 이거는 GPT한테 물어봤습니다...

실무에서 데드락 발생 시 어떻게 처리하는지가 궁금해서...

 

예방, 감지, 그리고 복구의 세 가지 접근 방식으로 나눌 수 있습니다

 

1. 예방 (Prevention)

데드락을 예방하는 방법은 문제 발생 자체를 줄이는 것

 

a. 트랜잭션 범위 최소화: 트랜잭션을 짧고 간단하게 유지하여 자원 점유 시간을 최소화함

b. 일관된 잠금 순서(순서 규칙 설정): 여러 트랜잭션이 동일한 자원을 잠글 때, 모든 트랜잭션이 동일한 순서로 자원을 잠그도록 함

c. 적절한 인덱스 사용: 인덱스를 사용하여 쿼리 성능을 향상시키고, 자원 점유 시간을 줄임

d. 트랜잭션 격리 수준 조정: 적절한 격리 수준을 선택하여 데이터의 일관성을 유지하면서 데드락 가능성을 줄임

 

 

2. 감지 (Detection)

데드락 감지는 시스템에서 데드락이 발생했는지 확인하는 것

a. 데드락 감지 알고리즘: 일반적으로 Wait-For 그래프(Wait-For Graph)(위에서 간단히 설명함)

b. 데드락 로그 분석: 데이터베이스 로그를 분석하여 데드락 발생 시점을 파악

 

 

3. 복구 (Recovery)

데드락이 발생한 경우, 이를 해결하기 위한 복구 방법은 다음과 같음

a. 트랜잭션 롤백: MySQL의 InnoDB 스토리지 엔진은 내부적으로 데드락 탐지 기능을 포함하고 있으며, 자동으로 교착 상태를 감지하고 트랜잭션 중 하나를 롤백함(위의 예제에서는 트랜잭션2가 자동 롤백됨)

b. 재시도 로직 구현: 애플리케이션에서 데드락이 발생했을 때 자동으로 트랜잭션을 롤백하고 재시도하는 로직을 구현

 

 

+) 

  • 데이터베이스 모니터링 도구: MySQL Workbench, pgAdmin (PostgreSQL), SQL Server Management Studio 등에서 데드락 모니터링 및 분석 기능을 제공하고 있음
  • 트랜잭션 관리: 대규모 데이터베이스 애플리케이션에서는 트랜잭션 관리를 정교하게 설정하여 데드락 발생 가능성을 줄이고, 발생 시 적절히 처리할 수 있도록 설계함