본문 바로가기

개발중/Java Persistence API (JPA)

[JPA] 낙관적 락(Optimistic Lock)과 비관적락 (Pessimistic Lock) 이란 ?

728x90
반응형

 

 

 
낙관적 락 ( Optimistic Lock )

비관적 락 ( Pessimistic Lock )
선택 기준

충돌 발생 확률이 낮은
상황에서 주로 사용됩니다. 이 방식은 데이터가 대부분의 시간 동안 변경되지 않을 것이라는 '낙관적' 가정 하에 작동합니다.
충돌이 자주 발생할 것으로 예상되는 상황, 예를 들어, 동시에 여러 트랜잭션이 같은 데이터를 변경할 가능성이 높은 경우에 사용됩니다.
충돌 대응
데이터를 업데이트하는 시점
에서만 충돌을 확인합니다. 만약 충돌이 발견되면, 일반적으로 트랜잭션을 재시도하거나 오류를 반환합니다.

데이터에 접근하기 전에 먼저 락을 걸어 다른 트랜잭션의 접근을 차단합니다. 이로써 충돌을 예방합니다.

사용 방법


낙관적 락은 버전 관리(Versioning) 등의 방법을 통해 구현할 수 있습니다. 
충돌이 감지되면, 업데이트가 거부되고 예외가 발생할 수 있습니다.


 LockModeType.PESSIMISTIC_WRITE와 같은 락 모드를 사용하여 데이터에 대한 배타적인 접근을 제어합니다. 
이 방식은 데이터의 일관성을 유지하기 위해 데이터베이스 자원에 대한 경쟁이 치열할 때 유용합니다.

 

낙관적 락 예제

 

낙관적 락은 데이터의 동시성을 처리할 때 데이터 충돌이 드물 것으로 예상되는 경우에 사용합니다. 

JPA(Java Persistence API)에서 낙관적 락을 구현하는 일반적인 방법은 엔티티에 버전 관리를 사용하는 것입니다. 

이를 위해 엔티티 클래스 내에 버전 필드를 @Version 애노테이션과 함께 선언합니다.

다음은 낙관적 락을 사용하기 위한 간단한 예제입니다:

 

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity
public class SomeEntity {
    @Id
    private Long id;
    
    private String data;
    
    @Version
    private Long version;

    // getters and setters
}

 

 

이 예제에서 SomeEntity 클래스는 version 필드를 가지고 있습니다. 

JPA는 @Version 애노테이션이 붙은 필드의 값을 트랜잭션이 커밋될 때마다 자동으로 증가시킵니다. 

엔티티가 업데이트되는 동안 해당 엔티티의 버전과 데이터베이스의 버전이 일치하지 않으면 JPA는 OptimisticLockException을 던지며, 이는 다른 트랜잭션이 이미 데이터를 변경했다는 것을 의미합니다.

 

try {
    // 엔티티 조회
    SomeEntity entity = entityManager.find(SomeEntity.class, entityId);
    
    // 엔티티 변경
    entity.setData(newData);
    
    // 트랜잭션 커밋
    entityManager.getTransaction().commit();
} catch (OptimisticLockException ole) {
    // 충돌이 발생했을 경우의 예외 처리
}

 

 

위 코드에서는 엔티티를 조회하고 데이터를 변경한 뒤 커밋을 시도합니다. 

만약 다른 트랜잭션이 이미 해당 엔티티를 변경하여 버전이 불일치하게 되면 OptimisticLockException이 발생하고, 이에 대한 적절한 처리를 할 수 있습니다. 

예외 처리에서는 사용자에게 충돌이 발생했음을 알리거나, 자동으로 재시도 로직을 구현할 수 있습니다.

낙관적 락은 데이터베이스 레벨에서 추가적인 락을 걸지 않으므로 성능상의 이점이 있지만, 충돌 발생 시 애플리케이션에서 충돌을 해결하는 로직을 구현해야 하는 단점이 있습니다.

 

비관적 락 예제

 

비관적 락(Pessimistic Lock)은 동시성을 관리하기 위해 JPA에서 사용되며, 특히 충돌 가능성이 높을 때 데이터 일관성을 보장하기 위해 적용됩니다. 

비관적 락을 사용할 때, 트랜잭션이 특정 데이터에 대한 접근을 시작할 때 데이터베이스 레벨에서 락을 걸어 다른 트랜잭션의 접근을 막습니다.

JPA에서 비관적 락을 구현하는 방법은 다음과 같습니다:

 

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;

public class SomeService {
    @PersistenceContext
    private EntityManager entityManager;

    public void someMethod(Long entityId) {
        try {
            // 비관적 락을 사용하여 엔티티 조회
            SomeEntity entity = entityManager.find(SomeEntity.class, entityId, LockModeType.PESSIMISTIC_WRITE);

            // 엔티티 변경
            entity.setData(newData);

            // 변경 내용을 데이터베이스에 반영하고 락 해제
            entityManager.flush();

        } catch (Exception e) {
            // 오류 처리, 예를 들어 잠금을 획득하지 못하는 경우
        }
    }
}

 

 

위 코드에서는 EntityManager의 find() 메소드를 사용하여 엔티티를 조회할 때 LockModeType.PESSIMISTIC_WRITE를 전달합니다. 

이는 해당 엔티티에 대한 배타적 락을 요청하고, 다른 트랜잭션이 해당 데이터를 변경하거나 읽지 못하게 합니다.

flush() 메소드는 변경 내용을 데이터베이스에 반영하고, 비관적 락을 해제합니다.

비관적 락을 사용할 때 주의해야 할 점은 데드락(Deadlock)이 발생할 가능성입니다. 

이는 여러 트랜잭션이 서로의 락을 기다리는 상태에 빠질 수 있기 때문입니다. 

따라서 오류 처리 부분에서 데드락을 감지하고 적절한 예외 처리를 구현해야 합니다.

 

데드락을 감지하는 방법은 ?

데드락을 감지하는 것은 데이터베이스 시스템의 관리 도구와 알고리즘에 크게 의존합니다.

대부분의 현대 데이터베이스 관리 시스템(DBMS)은 내부적으로 데드락 감지 기능을 가지고 있으며, 데드락이 발생하면 자동으로 감지하고 해결합니다.

 

데드락을 감지하고 해결하는 몇 가지 일반적인 방법은 다음과 같습니다

 

  • 타임아웃 설정
    • 트랜잭션이 지정된 시간 내에 완료되지 않으면 시스템이 데드락으로 간주하고 해당 트랜잭션을 중단시킬 수 있습니다. 이 방법은 단순하고 구현하기 쉽지만, 실제 데드락이 아닌 상황에서도 트랜잭션이 중단될 수 있다는 단점이 있습니다.
  • 데드락 감지 알고리즘
    • 많은 DBMS는 주기적으로 데드락을 감지하는 알고리즘을 실행합니다. 이 알고리즘은 대기 그래프(Wait-For Graph)를 구성하여 순환 대기(Circular Wait)를 확인함으로써 데드락을 감지합니다.
  • 자원 할당 그래프
    • 시스템이 자원 할당과 대기 상태를 추적하여 데드락 가능성을 감지합니다. 그래프에 순환 구조가 발생하면 데드락이 있는 것으로 간주됩니다.
  • 락 타임아웃
    • 특정 자원에 대한 락이 일정 시간 이상 지속될 경우 데드락이 발생했다고 판단하고 해당 트랜잭션을 종료시킬 수 있습니다.
  • 트랜잭션 로그 분석
    • 데이터베이스 로그를 분석하여 데드락 패턴을 식별할 수도 있습니다.


데드락이 감지되면, DBMS는 일반적으로 데드락을 해결하기 위해 하나 이상의 트랜잭션을 중단(롤백)합니다. 

이 과정에서 트랜잭션의 우선순위, 수행 시간, 자원 요구량 등 다양한 요소를 고려하여 롤백할 트랜잭션을 선택할 수 있습니다.

애플리케이션 레벨에서 데드락을 감지하는 것은 매우 복잡할 수 있으며, 대부분의 경우 DBMS에 의존하여 이 문제를 처리합니다. 

하지만 데드락이 감지되었을 때 애플리케이션에서 적절한 예외 처리를 할 수 있도록 SQLException 또는 특정 DBMS가 제공하는 예외를 캐치하고, 사용자에게 의미 있는 메시지를 제공하거나 트랜잭션을 재시도하는 로직을 구현할 수 있습니다.




728x90
반응형