낙관적 락과 비관적 락(MySQL)

민선규·2024년 5월 30일

데이터베이스

목록 보기
10/11
post-thumbnail

최근에 프로젝트를 진행하면서 동시성 문제를 발견했었고 이를 해결하기 위해 JPA의 낙관적 락을 적용한 경험이 있습니다. 이번 시간에는 JPA에서 제공하는 낙관적 락과 비관적 락에 대해서 자세히 학습해보았습니다.

JPA에서 제공하는 락

데이터베이스를 활용하여 데이터를 관리할 때 다양한 상황에서 동시성 문제를 겪을 수 있습니다. 그 중에서 Second Lost Updates Problem (두 번의 갱신 분실 문제)에 대해서 설명해보겠습니다.

Second Lost Updates Problem란 하나의 데이터에 여러 개의 트랜잭션이 동시에 수정하는 상황을 말합니다. 이런 상황을 해결하는 방법은 다음과 같으며 기본적으로는 마지막 커밋만 인정하기 방식을 사용합니다.

  • 마지막 커밋만 인정하기 : 유저A의 내용은 무시하고, 유저B가 변경한 내용으로 적용
  • 최초 커밋만 인정하기 : 유저A의 내용으로 적용하고, 유저B가 변경 시 오류 발생
  • 충돌하는 내용 병합하기 : 유저A, 유저B가 변경한 내용을 병합

하지만 때로는 최초 커밋만 인정하기 방식이 유효한 경우가 있습니다. 이는 트랜잭션 격리 수준 설정으로는 해결할 수 가 없습니다. 하지만 JPA에서 제공하는 락을 사용한다면 해결할 수 있습니다. JPA에서는 낙관적 락과 비관적 락을 지원하고 있습니다.

낙관적 락(Optimistic Lock)

먼저 낙관적 락에 대해서 정리해보겠습니다. 낙관적 락은 트랜잭션 충돌이 거의 발생하지 않을 거라고 가정하고 처리하는 방법입니다. 데이터베이스에서 제공하는 락을 사용하지 않고 어플리케이션 레벨에서 지원하는 락으로, Version을 통해 처리합니다.

@Version

Version을 통해 관리하는 방법은 엔티티의 필드를 추가하고 Version 어노테이션을 추가하면 됩니다. 여기서 타입으로 가능한것은 정수 또는 날짜 형식이 가능합니다.

@Entity
public class Item {
  @Id
  private String id;
  @Version
  private int version;
}

version의 값은 엔티티가 변경되면 자동으로 증가됩니다. 다만 엔티티가 변경될 때 조회 시점의 version값과 현재 version값이 동일한지 확인하고 동일하지 않으면 예외를 발생시킵니다.

(예외를 처리하는 작업이 필수적으로 필요합니다.)

LockModeType

NONE

별도로 설정하지 않고 Version 어노테이션만 설정하면 기본으로 적용되는 타입입니다.
조회한 엔티티를 수정하는 시점에 다른 트랜잭션으로부터 변경(또는 삭제)되지 않음을 보장합니다.

엔티티를 수정하는 시점에 엔티티의 버전을 증가시킵니다.

OPTIMISTIC

엔티티의 조회 시점부터 트랜잭션이 끝날 때 까지 다른 트랜잭션에 의해 변경되지 않음을 보장합니다.

NONE의 경우는 해당 엔티티를 수정해야만 버전을 체크하지만 OPTIMISTIC은 조회만으로 버전을 체크합니다.

OPTIMISTICFORCEINCREMENT

낙관적 락을 사용하면서 버전 정보를 강제로 증가시킵니다. 엔티티가 물리적으로 변경되지 않았지만, 논리적으로는 변경되었을 경우 버전을 증가하고 싶을 때 사용합니다.

비관적 락(Pessimistic Lock)

다음으로는 비관적 락입니다. 비관적 락은 낙관적 락에 반대로 트랜잭션 충돌이 자주 일어날거라 가정하고 처리하는 방법입니다. 실제 데이터베이스의 락을 사용하여 처리합니다.

LockModeType

PESSIMISTIC_WRITE

일반적으로 사용되는 옵션으로 데이터 베이스에 SELECT FOR UPDATE를 사용해서 락을 거는 타입입니다.

PESSIMISTIC_READ

데이터를 반복 읽기만 하고 수정하지 않는 용도로 락을 걸 때 사용하며 데이터베이스에 SELECT FOR SHARE를 사용해서 락을 거는 타입입니다.

PESSIMISTICFORCEINCREMENT

비관적 락중 유일하게 버전 정보를 사용하는 타입입니다.

참고 문서 및 링크

0개의 댓글