데이터베이스

[DB] Isolation & Lock

goblin- 2024. 11. 13. 23:35

트랜잭션과 관련된 락과 격리 수준(Isolation)은 데이터 일관성과 무결성을 유지하기 위한 핵심적인 개념들입니다. 이 두 가지를 이해하면 동시에 여러 트랜잭션이 실행될 때 데이터 충돌이나 불일치 상황을 어떻게 방지할 수 있는지 파악할 수 있습니다. 트랜잭션과 락, 격리 수준에 대한 카테고리를 다음과 같이 정리해보겠습니다:

 

1. 트랜잭션의 기본 개념

 

트랜잭션: 데이터베이스의 논리적인 작업 단위로, 트랜잭션은 완전하게 수행되거나 완전히 수행되지 않아야 합니다. 이는 ACID 특성(Atomicity, Consistency, Isolation, Durability)을 지키는 것을 목표로 합니다.

Atomicity(원자성): 트랜잭션 내 모든 작업이 성공하거나 모두 실패해야 함.

Consistency(일관성): 트랜잭션이 완료된 후 데이터가 일관된 상태여야 함.

Isolation(격리성): 트랜잭션이 독립적으로 실행되어야 하며, 다른 트랜잭션이 영향을 받지 않도록 해야 함.

Durability(내구성): 트랜잭션이 성공적으로 완료되면 결과가 영구적으로 저장되어야 함.

 

2. 격리 수준(Isolation Level)

 

격리 수준은 여러 트랜잭션이 동시에 실행될 때 발생할 수 있는 데이터 불일치 문제를 방지하기 위한 설정입니다. 격리 수준에 따라 성능과 일관성 간의 트레이드오프가 존재합니다. 주요 격리 수준과 각 수준에서 발생할 수 있는 문제는 다음과 같습니다:

READ UNCOMMITTED (커밋되지 않은 읽기)

가장 낮은 격리 수준으로, 트랜잭션이 커밋되지 않은 데이터를 다른 트랜잭션이 읽을 수 있습니다.

문제점: Dirty Read(더티 읽기) 발생 가능.

장점: 성능이 가장 뛰어나지만 일관성 유지가 어려움.

READ COMMITTED (커밋된 읽기)

다른 트랜잭션이 커밋한 데이터만 읽을 수 있는 수준입니다. 많은 데이터베이스에서 기본 격리 수준으로 사용됩니다.

문제점: Non-repeatable Read(반복 불가능한 읽기) 발생 가능.

장점: 성능과 일관성 간의 균형이 좋음.

REPEATABLE READ (반복 가능한 읽기)

트랜잭션이 시작될 때 조회한 데이터가 트랜잭션이 완료될 때까지 동일하게 유지됩니다. MySQL에서 기본 격리 수준입니다.

문제점: Phantom Read(팬텀 읽기) 발생 가능.

장점: Non-repeatable Read를 방지하여 데이터의 일관성이 더 강화됨.

SERIALIZABLE (직렬화 가능)

가장 높은 격리 수준으로, 트랜잭션이 직렬화되도록 실행되어 팬텀 읽기도 방지합니다.

문제점: 성능 저하 가능.

장점: 데이터 일관성을 가장 철저하게 보장하지만, 성능에 상당한 영향을 미칠 수 있음.

 

3. 락(Lock)

 

락은 트랜잭션 간 데이터 충돌을 방지하는 중요한 도구입니다. 트랜잭션이 특정 데이터에 락을 걸면, 다른 트랜잭션이 그 데이터에 접근할 수 없거나 제한적으로 접근하게 됩니다. 락의 종류는 다음과 같습니다:

 

공유 락(Shared Lock):

데이터를 읽기 위해 사용하는 락으로, 동시에 여러 트랜잭션이 공유 락을 획득할 수 있습니다.

주로 SELECT 쿼리에서 사용되며, 데이터의 일관성을 유지하는 데 유용합니다.

배타적 락(Exclusive Lock):

데이터를 수정하기 위해 사용하는 락으로, 하나의 트랜잭션만 배타적 락을 걸 수 있습니다.

주로 INSERT, UPDATE, DELETE와 같은 쓰기 작업에서 사용됩니다.

행 락(Row Lock):

특정 행(row) 단위로 걸리는 락으로, 테이블의 다른 행에는 영향을 주지 않으므로 효율적입니다.

테이블 락(Table Lock):

테이블 전체에 걸리는 락으로, 동시에 여러 행에 락을 걸기 어려운 경우 사용됩니다. 성능에 영향을 줄 수 있습니다.

 

4. 낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock)

 

낙관적 락은 데이터 충돌 가능성이 낮다고 가정하고 데이터 변경 시에만 충돌 여부를 검사하는 방식입니다. JPA에서는 @Version 필드를 사용하여 버전 관리를 통해 구현할 수 있습니다.

비관적 락은 데이터 충돌 가능성이 높다고 가정하여 데이터를 읽을 때 락을 거는 방식입니다. JPA에서 @Lock 어노테이션을 통해 PESSIMISTIC_READ 또는 PESSIMISTIC_WRITE를 지정할 수 있습니다.

사용 시기:

낙관적 락은 충돌이 적고, 성능이 중요한 경우 적합합니다.

비관적 락은 충돌이 잦고 데이터의 정확성이 최우선인 경우 적합합니다.

 

5. 데드락(Deadlock)과 타임아웃(Lock Timeout)

 

데드락(Deadlock): 두 트랜잭션이 서로의 자원을 대기하면서 무한히 대기하는 상황을 말합니다. MySQL은 데드락을 감지하면 자동으로 트랜잭션을 롤백하여 해결하려고 합니다.

타임아웃(Lock Timeout): 트랜잭션이 너무 오랫동안 락을 대기할 때 발생하며, 설정된 시간 내에 락을 획득하지 못하면 타임아웃 오류가 발생합니다.

해결 방법: 데드락과 타임아웃을 방지하려면 트랜잭션 범위를 최소화하고, 가능한 한 짧게 유지하는 것이 좋습니다.

 

6. Spring에서의 트랜잭션 전파 속성(Transaction Propagation)

 

트랜잭션이 다른 트랜잭션 내에서 호출될 때 트랜잭션 전파 속성을 지정하여 트랜잭션 경계를 관리할 수 있습니다.

REQUIRED: 현재 트랜잭션이 있으면 참여하고, 없으면 새 트랜잭션을 생성.

REQUIRES_NEW: 항상 새로운 트랜잭션을 생성하며, 기존 트랜잭션은 일시 정지.

SUPPORTS: 트랜잭션이 있으면 참여하고, 없으면 트랜잭션 없이 실행.

전파 속성 설정을 통해 트랜잭션 간의 충돌을 줄이고, 데이터 일관성을 유지하는 데 도움이 됩니다.

 

7. 성능 최적화 고려 사항

 

트랜잭션의 범위를 가능한 작게 설정하여 데이터베이스 락을 오래 유지하지 않도록 최적화하는 것이 좋습니다.

락이 걸리는 자원의 범위를 줄이고, 읽기 전용 트랜잭션에 대해 격리 수준을 낮춰서 성능을 최적화하는 방법도 고려할 수 있습니다.

 

이와 같은 카테고리로 트랜잭션과 락을 학습하면 Spring JPA와 MySQL 환경에서 트랜잭션 관리와 데이터 일관성을 유지하는 데 큰 도움이 될 것입니다.