낙관적 LOCK 과 비관적 LOCK - (JPA, Mysql)
먼저 알고 가면 좋은 것들
ACID
Transaction 격리수준
본론
JPA 는 기본적으로 데이터베이스 트랜잭션 격리 수준을 READ COMMITTED 정도로 가정한다.
만약 일부 로직에 더 높은 격리 수준이 필요하면 낙관적 락과 비관적 락 중에 하나를 사용하면 된다.
낙관적 LOCK
낙관적 lock 은 이름 그대로 트랜잭션 대부분은 충돌이 발생하지 않는다고 가정 하는 방법이다.
이것은 DB 에서 제공하는 LOCK 기능을 사용하는 것이 아니라 JPA 가 제공 하는 Version 관리 기능을 사용한다.
한마디로 DB 에서 LOCK 을 거는 것이 아닌 Application 단계에서 LOCK 을 거는 것이다.
비관적 LOCK
비관적 LOCK 은 트랜잭션의 충돌이 발생한다는 것을 가정하고 먼저 LOCK 을 거는 것이다.
대표적으로 select for update 가 있다.
JPA Version annotation
낙관적 LOCK 과 비관적 LOCK 을 설명하기 전에 먼저 @Version 을 알아보자.
낙관적 LOCK 을 사용하려면 Version 어노테이션을 사용해서 버전 관리를 해야한다.
적용 가능한 타입은 다음과 같다.
- Long
- Integer
- Short
- Timestamp
@Version
private Integer money;
Version 어노테이션이 붙은 field 는 엔티티를 수정할 때마다 버전이 하나씩 자동으로 증가한다.
그리고 수정할대 조회 시점의 버전과 수정 시점이 다르면 예외가 발생한다.
주의할점
@Version 으로 추가한 버전 관리 Field 는 JPA 가 직접 관리하므로 임의로 수정해서는 안된다. (벌크 연산을 제외하고는)
만약 버전 값을 강제로 증가하려면 특별한 LOCK 옵션을 선택하면 된다.
JPA 에서 LOCK 사용
LOCK 을 적용 할수 있는 곳
- EntityManger.lock()
- EntityManger.find()
- EntitiyManger.refresh()
- Query.setLockMode()
- @NamedQuery
JPA 낙관적 LOCK
OPTIMISTIC
@Version 만 적용했을 때는 엔티티를 수정해야 버전을 체크하지만 이 옵션을 추가하면 엔티티를 조회만 해도 버전을 체크한다.
한번 조회한 엔티티는 Transaction이 종료될때 까지는 다른 Transaction 에서 변경되지 않음을 보장한다.
사용 - 조회한 엔티티는 트랜잭션이 끝날 때 까지 다른 Transaction 에 의해 변경되지 않아야 한다.
조회 시점 부터 트랜잭션이 끝날 때 까지 조회한 엔티티가 변경되지않음을 보장
동작 - Transaction 을 커밋 할때 버전 정보를 조회해서 (Select query) 현재 엔티티 의 버전과 같은지 검증한다. 만약 같지 않으면 예외 발생
이점 - OPTIMISTIC 옵션은 DIRTY READ 와 NON-REPEATABLE READ 를 방지한다.
OPTIMISTIC_FORCE_INCREMENT
낙관적 LOCK 을 사용하면서 버전 정보를 강제로 증가한다.
사용 - 논리적인 단위의 엔티티 묶음을 관리할 수 있다. 예를 들어 게시물과 첨 부파일이 일대다, 다대일의 양방향 연관관계이고
첨부파일이 연관관계의 주인이다. 게시물을 수정하는 데 단순히 첨부파일만 추가하면 게시물의 버전은 증가하지 않는다.
해당 게시물은 물리적으로는 변경되지 않았지만, 논리적으로는 변경되었다.
이때 게시물의 버전도 강제로 증가하려면 OPTIMISTIC_FORCE_INCREMENT 를 사용하면 된다.
동작 - 엔티티를 수정하지 않아도 트랜잭션을 커밋할 때 UPDATE 쿼리를 사용해서 버전 정보를 강제로 증가시킨다.
이때 데이터베이스의 버전이 엔티티 의 버전과 다르면 예외가 발생한다.
추가로 엔티티를 수정하면 수정 시 버전 UPDATE가 발생한다. 따라서 총 2번의 버전 증가가 나타날 수 있다.
이점 - 강제로 버전을 증가해서 논리적인 단위의 엔티티 묶음을 버전 관리할 수 있다.
JPA 비관적 락
JPA가 제공하는 비관적 락은 데이터베이스 트랜잭션 락 메커니즘에 의존하는 방법이다.
주로 SQL 쿼리에 select for update 구문을 사용하면서 시작하고 버전 정보는 사용하지 않는다.
비관적 락은 주로 PESSIMISTIC_WRITE 모드를 사용한다.
비관적 락은 다음과 같은 특징이 있다.
- 엔티티가 아닌 스칼라 타입을 조회할 때도 사용할 수 있다.
- 데이터를 수정하는 즉시 트랜잭션 충돌을 감지할 수 있다.
PESSIMISTIC_WRITE
비관적 락이라 하면 일반적으로 이 옵션을 뜻한다. 데이터베이스에 쓰기 락을 걸때 사용한다.
사용 - 데이터베이스에 쓰기 락을 건다.
동작 - 데이터베이스 select for update 를 사용해서 락을 건다.
이점 - NON-REPEATABLE READ를 방지한다. 락이 걸린 로우는 다른 트랜잭션이 수정할 수 없다.
PESSIMISTIC_READ
데이터를 반복 읽기만 하고 수정하지 않는 용도로 락을 걸 때 사용한다. 일반적 으로 잘 사용하지 않는다.
데이터베이스 대부분은 방언에 의해 PESSIMISTIC_WRITE로 동작한다.
PESSIMISTIC_FORCE_INCREMENT
비관적 락중 유일하게 버전 정보를 사용한다. 비관적 락이지만 버전 정보를 강제
로 증가시킨다. 하이버네이트는 nowait 를 지원하는 데이터베이스에 대해서 for update nowait 옵션을 적용한다.
'Java > java - spring' 카테고리의 다른 글
JPA - Proxy (0) | 2022.07.02 |
---|---|
Connection Pool 이해하고 사용하기 (0) | 2022.06.25 |
상속 관계 매핑 Entity 를 QueryDsl 에서 사용하기 (0) | 2022.05.23 |
Failed to start bean 'documentationPluginsBootstrapper' 오류 해결 (0) | 2022.03.02 |
JPA 성능 최적화 - N + 1 문제 해결 하기 (0) | 2022.01.30 |
댓글
이 글 공유하기
다른 글
-
JPA - Proxy
JPA - Proxy
2022.07.02 -
Connection Pool 이해하고 사용하기
Connection Pool 이해하고 사용하기
2022.06.25 -
상속 관계 매핑 Entity 를 QueryDsl 에서 사용하기
상속 관계 매핑 Entity 를 QueryDsl 에서 사용하기
2022.05.23 -
Failed to start bean 'documentationPluginsBootstrapper' 오류 해결
Failed to start bean 'documentationPluginsBootstrapper' 오류 해결
2022.03.02