[DB] Isolation & Lock
트랜잭션과 관련된 락과 격리 수준(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 환경에서 트랜잭션 관리와 데이터 일관성을 유지하는 데 큰 도움이 될 것입니다.