락(Lock)은 여러 스레드나 트랜잭션이 동시에 데이터에 접근하는 경우 발생할 수 있는 동시성 문제를 방지하는 메커니즘이다.
락은 데이터 일관성을 보장하는 중요한 방법이며, 트랜잭션의 충돌을 방지하거나 해결하는 데 사용된다.
비관적 락 (Pessimistic Lock)
충돌이 무조건 발생할 것으로 가정하고, 트랜잭션 시작 시 데이터에 대해 미리 락을 거는 방식
주로 충돌이 자주 발생할 것으로 예상되는 상황에서 사용된다.
비관적 락은 다음과 같이 두 가지 형태로 나뉠 수 있다.
공유 락 (Shared Lock, 읽기 잠금)
SELECT FOR SHARE
쿼리를 통해 설정됨배타적 락 (Exclusive Lock, 쓰기 잠금)
- 다른 트랜잭션이 데이터를 읽거나 수정할 수 없으며, 락을 걸어둔 트랜잭션만 작업을 수행할 수 있음
- SELECT FOR UPDATE
쿼리를 통해 설정됨
JPA에서 비관적 락 적용 방법:
PESSIMISTIC_READ
: 공유 락을 사용하여 데이터 읽기는 허용하지만, 쓰기는 불가능하게 설정.
PESSIMISTIC_WRITE
: 배타적 락을 사용하여 데이터에 대한 읽기 및 쓰기를 모두 제한.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints(QueryHint(name = "jakarta.persistence.lock.timeout", value = "10000"))
fun findWriteLockByCouponKey(couponKey: Long): Coupon
낙관적 락 (Optimistic Lock)
충돌이 자주 발생하지 않는다고 가정하고, 트랜잭션이 종료될 때 충돌을 감지하여 처리하는 방식
데이터에 대한 실질적인 락을 걸지 않으며, 주로 버전 번호를 사용하여 충돌을 감지한다. 트랜잭션 중 데이터가 다른 트랜잭션에 의해 변경되었을 경우 예외(OptimisticLockException)를 발생시켜 충돌을 처리한다.
낙관적 락 VS 비관적 락 비교하기