---
JPA 동시성 처리할때도 블로그에는 비관락 써도 될것처럼 써놨는데 쓰지말자.
낙관락 -> 분산락 이렇게 가는게 맞음.
배타락은 최악이다.
라는 의견이 있었는데
---
왜 그런거지 ?? 해당 이유에 대해서 찾아보자.
비관락 : 실제로 DB 단에 X-Lock을 설정해서 동시성을 제어하는 방법
배타락(Exclusive Lock)이란?
한 번에 하나의 트랜잭션만 해당 자원에 접근할 수 있도록 하는 락입니다.
- 읽기와 쓰기 모두 차단
- 다른 트랜잭션은 해당 자원에 완전히 접근 불가
- 가장 강력한 형태의 락
-- MySQL/PostgreSQL
SELECT * FROM user WHERE id = 1 FOR UPDATE;
-- Oracle
SELECT * FROM user WHERE id = 1 FOR UPDATE;
-- SQL Server
SELECT * FROM user WITH (UPDLOCK, ROWLOCK) WHERE id = 1;
배타락 vs 공유락 비교
// 배타락
@Lock(LockModeType.PESSIMISTIC_WRITE) // X-Lock
User findByIdForUpdate(Long id);
// 공유락
@Lock(LockModeType.PESSIMISTIC_READ) // S-Lock
User findByIdForRead(Long id);
---
비관적 락 ≠ 배타락
**비관적 락(Pessimistic Lock)**은 락킹 전략이고,
**배타락(Exclusive Lock)**은 락의 종류입니다.
** 위 내용에 대한 개념 구분을 구분해보자. **
락킹 전략 (언제 락을 걸 것인가?)
// 비관적 락: "충돌이 일어날 것이다" - 미리 락을 걸자
@Lock(LockModeType.PESSIMISTIC_WRITE)
// 낙관적 락: "충돌이 거의 없을 것이다" - 커밋할 때 체크하자
@Version
private Long version;
락의 종류 (어떤 방식으로 락을 걸 것인가?)
// 배타락 (X-Lock): 읽기/쓰기 모두 차단
SELECT * FROM user WHERE id = 1 FOR UPDATE;
// 공유락 (S-Lock): 읽기는 허용, 쓰기만 차단
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE;
JPA에서의 비관적 락 종류
// 비관적 락 + 배타락
@Lock(LockModeType.PESSIMISTIC_WRITE) // X-Lock 사용
User findByIdForUpdate(Long id);
// 비관적 락 + 공유락
@Lock(LockModeType.PESSIMISTIC_READ) // S-Lock 사용
User findByIdForRead(Long id);
정리하면
* 비관적 락 : 락킹 전략 (미리 락을 검) PESSIMISTIC_WRITE, PESSIMISTIC_READ
* 배타락 : 락의 종류 (읽기/쓰기 모두 차단) FOR UPDATE
* 공유락 : 락의 종류 (읽기 허용, 쓰기 차단) LOCK IN SHARE MODE
따라서
비관적 락을 걸었다 = 미리 락을 걸었다
배타락을 걸었다 = 읽기/쓰기 모두 차단하는 락을 걸었다
비관적 락 전략으로 배타락을 사용할 수도 있고, 공유락을 사용할 수도 있습니다!
---
낙관적 락으로 시작
낙관적 락은 데이터베이스 수준에서 락을 걸지 않기 때문에 데드락은 발생하지 않는다.
데드락 대신, 낙관적 락은 충돌 감지(conflict detection) 방식으로 작동
커밋 시점에 버전 충돌이 발생하면 예외가 발생하고 트랜잭션이 롤백
-> 이는 데이터 일관성을 유지하면서도 동시 접근을 허용하여 성능을 향상
성능 문제나 높은 충돌률이 발생하면 비관적 락 고려 (하지만 신중하게)
비관적 락은 데이터베이스 수준에서 행(row)을 잠그는 방식으로 작동하며,
이로 인해 애플리케이션 성능이 저하될 수 있다.
특히 트래픽이 많은 환경에서는 데드락(deadlock)이 발생할 가능성도 있어서 주의해서 사용
분산 환경에서는 Redis/Redisson과 같은 분산 락 메커니즘 도입
Spring에서 분산락을 구현하는 몇 가지 일반적인 방법
커스텀 어노테이션 + AOP: @DistributedLock과 같은 커스텀 어노테이션을 만들고
AOP를 사용하여 해당 어노테이션이 적용된 메소드가 실행될 때 분산락을 획득하고 해제
또는, redisson을 직접 사용
RLock lock = redisson.getLock("myLock");
lock.lock();
try {
// 임계 영역 코드 실행
} finally {
lock.unlock();
}