비관적 락은 데이터에 접근할 때부터 미리 락을 걸어 다른 트랜잭션의 접근을 막는 방식입니다.
반면, 낙관적 락은 별도로 락을 걸지 않고, 작업이 끝난 후 커밋 직전에 충돌을 검사하여 문제가 있으면 롤백하는 방식입니다.
비관적 락은 충돌 가능성이 높고 데이터 무결성이 중요한 환경에, 낙관적 락은 충돌 가능성은 낮으면서 높은 동시성이 필요한 환경에 적합합니다.
개념 설명
비관적 락 (Pessimistic Lock)
데이터를 읽거나 수정할 때 다른 트랜잭션의 접근을 차단
구현 방법
SQL 레벨: SELECT ... FOR UPDATE 구문 직접 사용
JPA 레벨: @Lock(LockModeType.PESSIMISTIC_WRITE) 어노테이션 사용
(→ JPA가 내부적으로 FOR UPDATE 쿼리를 생성해줌)
LockModeType
설명
PESSIMISTIC_READ
읽기 락, 다른 트랜잭션의 읽기는 허용하지만 수정을 막음
PESSIMISTIC_WRITE
쓰기 락, 읽기와 수정을 모두 막음
PESSIMISTIC_FORCE_INCREMENT
쓰기 락 + 버전 강제 증가 (낙관적 락과 혼합 사용 가능)
주의할 점
데드락 발생 가능성
→ 여러 트랜잭션이 서로 락을 기다리다 교착 상태 발생 가능
성능 저하 가능성
→ 락을 건 트랜잭션이 길어지거나, 많은 트랜잭션이 대기하면 전체 시스템 성능 저하
락 범위 최소화 필요
→ 필요한 범위까지만 락을 걸어 대기 최소화
낙관적 락 (Optimistic Lock)
데이터 조회는 자유롭게 허용 (락 없음)
수정 시 버전(version) 필드나 타임스탬프(timestamp)를 이용하여 트랜잭션 시작 시 읽은 버전 값을 저장해두고, 커밋 직전에 현재 버전과 비교하여 일치하는지 검사
JPA에서는 @Version 어노테이션 사용 → 충돌 시 OptimisticLockException 발생
주의할 점
충돌 발생 시 롤백 및 재시도 필요
→ 트랜잭션 충돌 발생 시 재시도 로직을 따로 작성하거나, 사용자에게 재입력 요청
충돌율이 높으면 오히려 비효율
→ 충돌율이 높은 시스템에서는 낙관적 락이 성능에 악영향 줄 수 있음
버전 관리 정확성 필요
→ 버전 필드가 제대로 관리되지 않으면 데이터 무결성 보장 실패
비관적 락 vs 낙관적 락
구분
비관적 락 (Pessimistic Lock)
낙관적 락 (Optimistic Lock)
락 시점
데이터 읽을 때 락
커밋 직전에 충돌 검사
접근 제한
다른 트랜잭션 접근 차단
접근은 허용, 충돌 시 롤백
충돌 처리
충돌 자체를 방지
충돌 발생하면 롤백/재시도
적합한 경우
충돌 가능성 높음, 무결성 중요
(ex. 금융 이체 시스템, 재고 관리)
충돌 가능성 낮음, 무결성 상대적으로 덜 중요
(ex. 게시판 수정)
성능
락 대기가 많아지면 성능 저하
충돌 적으면 성능 우수
추가 질문
비관적 락에서 데드락은 왜 발생하고, 어떻게 방지할 수 있나요?
비관적 락은 트랜잭션이 데이터에 접근할 때 미리 락을 걸기 때문에, 여러 트랜잭션이 서로 상대방의 락 해제를 기다리는 데드락(교착 상태) 이 발생할 수 있습니다.
이를 방지하기 위해서 락을 획득하는 순서를 일관되게 유지하거나, 타임아웃 기반의 락 획득 전략을 사용하여 일정 시간 동안 락을 획득하지 못하면 트랜잭션을 종료시키는 방식으로 처리합니다.
낙관적 락에서 충돌이 발생하면 어떻게 대응하나요?
낙관적 락에서 충돌이 발생했다면, 해당 트랜잭션은 롤백되고, 예외가 발생합니다.
대응 방법으로는, 애플리케이션 로직에서 예외를 감지하고 재시도 로직을 구현하거나, 사용자에게 변경 내용이 충돌되었음을 알리고 재입력을 요청하는 방식이 있습니다.