데이터베이스 락(Lock)

.·2024년 10월 20일
0

DB

목록 보기
1/2

1. 락의 개념

  • 데이터베이스 락이란?
    • 데이터베이스에서 동시성을 제어하기 위한 기능
    • 여러 트랜잭션이 동시에 동일한 데이터를 접근하려고 할 때 발생할 수 있는 데이터 손실이나 불일치 문제를 방지하기 위해 사용

락은 데이터의 무결성과 일관성을 보장하는 중요한 수단으로 만약 락이 없다면 두 트랜잭션이 같은 데이터를 동시에 수정하려 할 때 충돌이 발생하거나, 중간 상태의 데이터를 읽음으로써 예기치 못 한 결과가 나올 수 있다.

⇒ 따라서 락은 여러 트랜잭션이 데이터베이스에서 안전하게 작업할 수 있도록 보호하는 역할

동시성 문제 예시

계좌 잔액이 1,000,000원인 상황에서 두 개의 트랜잭션이 동시에 발생한다고 가정

  • 트랜잭션 A: 500,000원 출금
  • 트랜잭션 B: 300,000원 입금

락이 없다면:

  • A와 B가 동시에 1,000,000원을 읽습니다.
  • A는 500,000원을 뺀 500,000원으로 업데이트합니다.
  • B는 300,000원을 더한 1,300,000원으로 업데이트합니다.
  • 최종 잔액은 1,300,000원이 되어, 실제로는 800,000원이 되어야 하는 잔액이 잘못 계산됩니다.

락을 사용하면:

  • A가 먼저 락을 획득하고 작업을 수행한 후 500,000원으로 업데이트합니다.
  • B는 A의 작업이 끝날 때까지 기다린 후 500,000원에 300,000원을 더해 800,000원으로 정확히 업데이트합니다.

2. 락의 종류

락의 종류는 크게 공유 락과 배타적 락으로 나눌 수 있다.

1) Shared Lock (공유 락)

  • 여러 트랜잭션이 동시에 데이터 조회는 가능하지만, 수정은 불가능하게 잠그는 것을 말한다.
  • 즉, 동시에 데이터를 조회할 수 있는 경우 사용된다.

예시

SELECT * FROM orders WHERE id = 123 FOR SHARE;

위 쿼리는 id=123인 행을 공유 락으로 잠급니다. 다른 트랜잭션은 이 데이터를 읽을 수 있지만 수정할 수는 없습니다.

⇒ 재고 조회 등에서는 해당 데이터를 읽어도 데이터의 정합성이 지켜지기 때문에 수정을 방지하고 조회만 가능하게 공유 락을 건다.

2) Exclusive Lock (배타적 락)

  • 데이터를 변경하는 작업을 위해 잠그는 것을 말한다.
  • 하나의 트랜잭션만 데이터에 접근할 수 있도록 보장한다.
    • 배타적 락이 걸리면 해당 데이터를 다른 트랜잭션이 읽거나 수정할 수 없다.
  • 주로 데이터 수정 작업 시 사용된다.

예시

UPDATE orders SET status = 'shipped' WHERE id = 123;
=> UPDATE에서 자동으로 적용

위 쿼리는 id=123인 행에 배타적 락을 걸어 해당 트랜잭션이 끝날 때까지 다른 트랜잭션은 이 행에 접근할 수 없습니다.

⇒ 은행 계좌 입출금 등에서는 해당 데이터를 읽거나 쓰기를 한다면 작업 결과가 달라질 수 있기 때문에 정합성이 지켜지지 않으므로 배타 락을 건다.

3) Row-Level Lock vs Table-Level Lock

  • Row-Level Lock (행 단위 락): 특정 행(row)에만 락을 걸어 다른 트랜잭션이 해당 행에만 접근을 못 하게 하는 방식이다. 테이블 전체가 아닌 일부 데이터만 수정하려는 경우에 유용하다.
  • Table-Level Lock (테이블 단위 락): 테이블 전체에 락을 걸어 다른 트랜잭션이 해당 테이블에 있는 모든 데이터에 접근하지 못하게 한다. 주로 테이블 구조 변경 시 사용되며, 대규모 업데이트가 필요할 때 사용한다.

4) Optimistic Lock vs Pessimistic Lock

이 두 방식은 공유 락이나 배타적 락과 달리 구현 전략에 가깝다. 즉, 데이터베이스가 아닌 애플리케이션에서 구현되는 경우가 많다.

  • Optimistic Lock (낙관적 락): 트랜잭션이 거의 충돌하지 않는다는 가정하에 락을 최소화합니다. 데이터 변경 시점에서 충돌을 검증하여 문제가 발생하면 다시 처리하는 방식이다. 주로 버전 관리(version control)를 통해 충돌 여부를 확인합니다.
  • Pessimistic Lock (비관적 락): 트랜잭션 충돌이 자주 발생할 것으로 예상되는 경우, 데이터에 접근할 때부터 락을 걸어 충돌을 방지한다. 데이터에 대한 쓰기 작업이 빈번한 환경에서 자주 사용된다.

3. 데드락(Deadlock)

  • 두 개 이상의 트랜잭션이 서로가 가지고 있는 락을 기다리면서 무한정 대기 상태에 빠지는 현상
  • 트랜잭션 A가 자원 X에 락을 걸고 자원 Y에 락을 요청하고, 트랜잭션 B가 자원 Y에 락을 걸고 자원 X에 락을 요청하는 상황에서 두 트랜잭션 모두 자원이 반활될 때까지 기다리게 되어 교착 상태가 발생

데드락 발생 예시

  1. 트랜잭션 A가 Resource 1을 잠금.
  2. 트랜잭션 B가 Resource 2를 잠금.
  3. 트랜잭션 A는 Resource 2를 요청하지만, B가 소유 중이므로 대기.
  4. 트랜잭션 B는 Resource 1을 요청하지만, A가 소유 중이므로 대기.

데드락 방지 및 해결 방법

  • 타임아웃(Timeouts): 일정 시간이 지나면 트랜잭션을 강제로 롤백시키는 방법이다. 데드락 상태를 오래 유지하지 않도록 트랜잭션 대기 시간을 제한한다. ⇒ MySQL 지원
  • 락 순서 보장: 트랜잭션이 자원을 잠글 때 일정한 순서를 정해 충돌을 방지하는 방법입니다. 예를 들어, 모든 트랜잭션이 자원 1을 먼저 락한 후 자원 2를 락하는 방식으로 데드락을 방지합니다. . ⇒ 개발자가 제어
  • 사이클 감지: 그래프 기반으로 트랜잭션과 락 간의 대기 관계를 분석하여 데드락 사이클을 발견하면 해당 트랜잭션을 롤백하는 방법이다. . ⇒ MySQL 지원

그 외에도 낙관적 락, 대기 그래프, 은행원 알고리즘 등이 있다.

4. 락 대기와 성능 이슈

락 경합(Contention)

  • 여러 트랜잭션이 동시에 동일한 자원에 접근하려고 할 때 발생
  • 락 경합이 자주 발생하면 데이터베이스 성능이 급격히 저하될 수 있다.

락 경합이 발생하는 주요 원인

  1. 트랜잭션이 너무 오래 락을 유지하는 경우
  2. 특정 데이터에 접근하는 트랜잭션이 많아 락 대기가 길어지는 경우
  3. 잘못된 트랜잭션 설계로 인해 불필요하게 많은 락을 사용하는 경우

락 에스컬레이션(Lock Escalation)

  • 다수의 행에 락이 걸릴 경우 테이블 전체에 락을 거는 현상과 그로 인한 성능 저하 문제.
  • 다수의 행에 락이 걸렸을 때 성능 최적화를 위해 자동으로 테이블 전체 락이 걸리지만 다른 트랜잭션의 접근이 차단될 수 있다.

락 에스컬레이션 문제 해결 방안

  1. 트랜잭션 크기를 최소화하고, 필요한 범위에서만 락을 거는 방법을 사용
  2. 인덱스 활용을 통해 행 단위로 락을 거는 범위를 줄이는 방법
  3. 애플리케이션 레벨에서 락을 줄이는 설계 ⇒ Redis, 데이터 분리 등

정리

데이터베이스 레벨에서의 락은 기본적으로 동시성 문제를 해결하는 중요한 방법이지만, 락 에스컬레이션과 같이 성능 저하나 많은 요청으로 인한 병목 현상이 발생할 수 있고 특히 대규모 환경과 같이 분산 환경에서는 더 큰 문제가 될 수 있다.

⇒ 이러한 문제를 해결하기 위해서는 Redis와 같은 인메모리 데이터 저장소를 활용하여 락을 중앙에서 관리하는 방식이 성능과 확장성 측면에서는 더 나은 선택이 될 수 있다.

profile
Tech

0개의 댓글