락(Lock)은 데이터베이스에서 동시성 문제를 해결하기 위한 메커니즘으로, 여러 트랜잭션이 동시에 같은 데이터를 읽거나 쓰려고 할 때 충돌이나 데이터 일관성 문제를 방지하기 위해 사용됩니다. 즉, 데이터에 대한 접근을 제한하거나 순서를 제어함으로써 데이터의 정합성을 유지하는 역할을 합니다.
락의 주요 목적:
- 데이터 일관성 보장: 여러 트랜잭션이 동시에 같은 데이터를 수정하려고 할 때, 데이터가 엉키거나 잘못된 상태가 되는 것을 방지합니다.
- 동시성 제어: 다수의 사용자가 동시에 데이터를 읽거나 수정하는 경우, 트랜잭션 간의 충돌을 피하고 안전한 데이터 처리를 보장합니다.
락의 종류
공유 락 (Shared Lock, S Lock)
- 읽기 전용 락으로, 데이터를 읽는 트랜잭션이 공유 락을 획득하면 다른 트랜잭션도 읽기는 가능하지만, 수정은 불가능합니다.
- 여러 트랜잭션이 동시에 읽기는 가능하지만, 수정하려는 트랜잭션은 대기해야 합니다.
배타 락 (Exclusive Lock, X Lock)
- 쓰기 전용 락으로, 트랜잭션이 데이터를 수정할 때 사용하는 락입니다. 배타 락을 획득한 트랜잭션이 있을 때는 다른 트랜잭션이 그 데이터를 읽거나 수정할 수 없습니다.
- 한 트랜잭션이 배타 락을 가진 동안, 다른 트랜잭션은 해당 데이터에 대한 접근이 차단됩니다.
락의 동작 예시
1. 공유 락(S Lock) 예시
- 트랜잭션 A가 공유 락을 획득하고 데이터를 읽습니다.
- 동시에 트랜잭션 B도 같은 데이터에 공유 락을 획득하여 데이터를 읽을 수 있습니다.
- 하지만 트랜잭션 C가 해당 데이터를 수정하려고 하면 배타 락을 요청해야 하고, 공유 락이 해제될 때까지 기다려야 합니다.
2. 배타 락(X Lock) 예시
- 트랜잭션 A가 배타 락을 획득하고 데이터를 수정하고 있습니다.
- 다른 트랜잭션은 해당 데이터를 읽거나 수정할 수 없으며, 트랜잭션 A가 작업을 완료하고 배타 락을 해제할 때까지 대기해야 합니다.
락과 격리 수준의 관계
락은 트랜잭션의 격리 수준에 따라 자동으로 적용되기도 합니다. 격리 수준이 높아질수록 데이터 충돌을 방지하기 위해 락이 더 많이 걸리며, 동시성 제어가 엄격해집니다.
- READ UNCOMMITTED: 락을 거의 사용하지 않습니다. 다른 트랜잭션이 데이터를 수정 중일 때도 읽을 수 있습니다.
- READ COMMITTED: 쓰기 작업에만 락을 사용하여, 다른 트랜잭션이 수정 중인 데이터를 읽지 않도록 합니다.
- REPEATABLE READ: 트랜잭션 동안 읽기 락(S Lock)을 사용하여 데이터가 변경되지 않도록 보장합니다.
- SERIALIZABLE: 가장 엄격한 락을 사용하여 트랜잭션 간 충돌을 완전히 방지합니다. 트랜잭션이 끝날 때까지 데이터를 읽거나 쓰지 못하게 막습니다.
락의 문제점
교착 상태 (Deadlock):
- 두 개 이상의 트랜잭션이 서로 상대방이 해제하기를 기다리면서 무한정 대기하는 상태입니다. 이를 방지하려면 적절한 순서로 락을 획득하거나 타임아웃 설정이 필요합니다.
락 경합 (Lock Contention):
- 여러 트랜잭션이 동시에 동일한 데이터에 접근하려고 할 때 발생하는 현상입니다. 락이 걸린 데이터에 대한 대기가 길어져 성능이 저하될 수 있습니다.
낮은 성능:
- 락을 많이 사용하면 동시 처리 성능이 떨어질 수 있습니다. 특히 높은 격리 수준을 사용할수록 락이 자주 발생하므로 성능이 저하될 수 있습니다.
Spring에서 락 사용 예시
스프링에서는 @Transactional
어노테이션과 함께 데이터베이스 트랜잭션에서 락을 제어할 수 있습니다. 특히 Pessimistic Lock(비관적 락)과 Optimistic Lock(낙관적 락)의 두 가지 락 전략을 사용할 수 있습니다.
1. 비관적 락(Pessimistic Lock)
비관적 락은 데이터를 조회할 때 바로 락을 걸어서 다른 트랜잭션이 해당 데이터를 수정하지 못하도록 방지하는 방식입니다.
import org.springframework.data.jpa.repository.Lock;
import javax.persistence.LockModeType;
public interface PersonRepository extends JpaRepository<Person, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE) // 비관적 락 적용
Optional<Person> findById(Long id);
}
2. 낙관적 락(Optimistic Lock)
낙관적 락은 데이터가 수정될 때만 충돌을 체크하는 방식입니다. 데이터 수정이 빈번하지 않은 상황에서 효율적으로 사용할 수 있습니다.
낙관적 락은 JPA의 @Version
어노테이션을 사용하여 구현합니다.
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Version // 낙관적 락을 위한 버전 필드
private Integer version;
}
낙관적 락은 버전 필드를 통해 수정 중에 다른 트랜잭션이 해당 데이터를 수정했는지 확인하며, 충돌이 발생하면 예외를 던집니다.
요약
- 락(Lock)은 데이터의 일관성과 무결성을 유지하기 위해 트랜잭션 간의 데이터 접근을 제어하는 메커니즘입니다.
- 공유 락은 읽기 작업에서 사용되고, 배타 락은 쓰기 작업에서 사용됩니다.
- 락을 너무 많이 사용하면 성능이 저하될 수 있으며, 교착 상태 같은 문제가 발생할 수 있습니다.
- 비관적 락과 낙관적 락은 트랜잭션 간 동시성 문제를 해결하는 주요 방법입니다.
'Spring Framework' 카테고리의 다른 글
오토 컨피규레이션(Auto-Configuration) 종류별 (0) | 2024.10.24 |
---|---|
JIT (Just-In-Time) 컴파일러와 AOT (Ahead-Of-Time) 컴파일러 (0) | 2024.10.24 |
Enable MVC Configuration (0) | 2024.10.16 |
리졸버(Resolver) (0) | 2024.10.16 |
디스패처 서블릿 동작과정 - 인터셉터 활용해서 알아보기 (0) | 2024.10.16 |