[JPA] 낙관적 Lock, 비관적 Lock
개요
JPA에는 동시에 여러 사용자가 접근하여 데이터를 조작할 때 발생할 수 있는 문제를 다루기 위해 Lock을 지원하고 있습니다.
이 중에 낙관적 Lock(Optimistic Lock), 비관적 Lock(Pessimistic Lock)에 대해서 알아보려 합니다.
낙관적 Lock
낙관적이란 단어에서 알 수 있듯이 데이터 갱신 시 충돌이 발생하는 것을 막는 것이 아닌 충돌이 일어났을 때 처리하는 Lock입니다.
@Entity
class Book(
@Id
val bookId: String,
val bookName: String,
@Version
val version: Int
)
JPA에서는 위와 같이 Entity에 version 필드를 지정하여 사용합니다. version 값을 통해서 Entity의 변경사항을 감지하고 이 값이 일치하지 않을 시 문제가 생겼다고 판단하는 방식을 사용합니다.
Select 후 트랜잭션이 업데이트를 처리하기 전에 version 값을 다시 확인합니다. 그 과정에서 version 값이 일치하지 않으면 OptimisticLockException을 발생시킵니다.
낙관적 Lock의 Mode
NONE
모드를 특별히 적용하지 않고 Entity에 @Version 어노테이션이 적용된 필드가 있다면 낙관적 Lock이 적용됩니다.
OPTIMISTIC(== READ)
낙관적 Lock은 기본적으로 데이터 갱신(Update) 시에만 적용됩니다. Optimistic 모드를 사용하면 조회(Read) 시에도 적용됩니다.
OPTIMISTIC_FORCE_INCREMENT(== WRITE)
기본적인 낙관적 Lock을 적용시키면서 Version 값을 강제로 증가시키는 모드입니다.
비관적 Lock
낙관적 Lock과는 반대로 충돌이 발생하는 것을 미연에 방지하는 것을 목표로 하는 Lock입니다. 데이터 정합성을 지키는데 낙관적 Lock보다 좋지만 성능상 안 좋을 수 있습니다.
비즈니스 로직 상에서는 비관적 Lock은 낙관적 Lock보다도 쉽게 적용 가능합니다. 비관적 Lock은 데이터베이스 수준의 잠금을 포함하기 때문입니다. 그렇지만 락을 획득하고 해제하는 것을 적절히 처리해야 하는 방식입니다. 사용 방법은 아래와 같이 @Lock 어노테이션을 활용합니다.
interface BookRepository: JpaRepository<Book, String> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select b from Book b where b.bookId = :bookId")
fun findByBookIdForUpdate(bookId: String): Book
}
비관적 Lock의 Mode
PESSIMISTIC_READ
다른 트랜잭션에게 읽기만 허용하는 모드입니다. DB의 Shared Lock을 이용해서 Lock을 적용합니다. 단, Shared Lock을 지원하지 않으면 PESSIMISTIC_WRITE와 동일하게 작동합니다.
PESSIMISTIC_WRITE
Row Exclusive Lock을 사용해서 Lock을 적용하고 다른 트랜잭션에서 읽기, 쓰기 모두 제한됩니다.
PESSIMISTIC_FORCE_INCREMENT
PESSIMISTIC_WRITE와 동일하게 Row Exclusive Lock을 사용하고 Lock 적용 동시에 version 값을 증가시킵니다.
정리
낙관적 락 (Optimistic Lock) | 비관적 락 (Pessimistic Lock) | |
동작 원리 | 버전(Version)이나 타임스탬프를 사용하여 충돌을 감지 | 레코드나 트랜잭션을 명시적으로 Lock |
Lock 획득 시점 | 트랜잭션 커밋 시에 Lock 획득 | 트랜잭션 시작 시에 Lock 획득 |
Lock 충돌 발생시 | 충돌 감지 후 롤백 및 예외 발생 | 락 충돌 시 다른 트랜잭션이 기다림 |
데이터 수정시 Lock 여부 | 데이터 수정 시에 Lock을 걸지 않음 | 데이터 수정 시에 Lock을 걸어 다른 트랜잭션이 기다림 |
성능 | 낙관적 Lock은 리소스 경쟁이 적고 Lock으로 인한 성능 저하가 적다 | 비관적 Lock은 오버헤드로 인한 성능 저하가 높음 |
트랜잭션 동시성 | Lock 충돌이 발생할 가능성이 높다 | Lock 충돌은 적지만 Lock을 획득한 트랜잭션을 기다려야만 다음 트랜잭션 처리 가능 |