코딩공작소

JPA 프로그래밍 학습 정리 (13) - 트랜잭션과 락, 2차 캐시 본문

어플리케이션개발/JPA

JPA 프로그래밍 학습 정리 (13) - 트랜잭션과 락, 2차 캐시

안잡아모찌 2024. 3. 10. 17:53

트랜잭션

ACID

  • 원자성 : 하나의 작업 처럼 모두 성공해야 한다
  • 일관성 : 무결성 제약 조건을 통해 일관성을 유지해야 한다
  • 격리성 : 동시에 같은 데이터를 수정하지 못하게 막아야 한다
  • 지속성 : 항상 기록해서 성공한 트랜잭션 내용을 복구해야 한다

 

트랜잭션에 격리 수준이 나누어져 있다.

  • READ UNCOMMITED (커밋되지 않은 읽기)
  • READ COMMITTED (커밋된 읽기)
  • REPEATABLE READ (반복 가능한 읽기)
  • SERIALIZABLE (직렬화 가능)

 

격리 수준에 따른 문제점이 있다.

  • DIRTY READ
  • NON-REPEATABLE READ (반복 불가능한 읽기)
  • PHANTOM READ

 

READ UNCOMMITTED

수정하고 있는데 커밋하지 않아도 데이터를 조회할 수 있다. 이것을 DIRTY READ라고 한다. 이것을 허용하는 격리이다.

 

READ COMMITTED

반복해서 같은 데이터를 읽을 수 없는 상태를 NON-REPEATABLE READ라 하고 이걸 허용하는 격리이다.

 

REPEATABLE READ

반복 조회 시 결과 집합이 달라지는 것을 PHANTOM READ라 하며 이걸 허용하는 격리이다.

 

SERIALIZABLE

동시성 처리 성능이 급격히 떨어질 수 있는 가장 엄격한 수준의 격리 수준이다.

 

기본 디폴트는 READ COMMITTED 격리 이다.

 

 

JPA 영속성 컨텍스트(1차 캐시)를 적절히 사용하면 반복 가능한 읽기가 가능하다.

낙관적 락은 이름 그대로 트랜잭션 대부분은 충돌이 발생하지 않는다고 낙관적으로 가정 하는 방법이다.

비관적 락은 이름 그대로 트랜잭션의 충돌이 발생한다고 가정하고 우선 락을 걸고 보는 방법이다.

 

 

두 번의 갱실 분실 문제라는 게 있다. 2명의 사용자가 수정을 하면 마지막의 내용만 커밋되는 문제이다.

이를 해결하기 위해서는 3가지 선택 방법이 있다.

  • 마지막 커밋만 인정하기
  • 최초 커밋만 인정하기
  • 충돌하는 갱신 내용 병합하기

기본은 마지막 커밋만 인정하기이다.

 

 

@Version

어노테이션을 통해 버전 관리 기능을 추가해야 한다.

적용 가능한 타입은 다음과 같다.

  • Long
  • Integer
  • Short
  • Timestamp

버전 정보를 사용하면 최초 커밋만 인정한다.

 

 

버전 정보 비교 방법

버전은 엔티티의 값을 변경하면 증가한다.

 

JPA 락 사용

락은 다음 위치에 적용할 수 있다.

  • EntityManager.lock(), EntityManager.find(), EntityManager.refresh()
  • Query.setLockMode() (TypeQuery 포함)
  • @NamedQuery

 

JPA 낙관적 락

JPA제공하는 낙관적 락은 버전을 사용한다. 이 방법은 커밋하는 시점에 충돌을 알 수 있다.

  • OptimisticLockException (JPA예외)
  • StaleObjectStateException (하이버네이트 예외)
  • ObjectOptimisticLockingFailureException (스프링 예외 추상화)

 

NONE

조회 시점부터 수정 시점까지를 보장한다. 데이터베이스의 버전 값이 현재 버전이 아니면 예외가 발생한다.

 

OPTIMISTIC

엔티티를 조회만 해도 버전을 체크한다.

트랜잭션을 커밋할 때 버전 정보를 조회해서 같지 않다면 예외가 발생한다. 엔티티를 수정하지 않고 단순히 조회만 해도 버전을 확인한다.

 

 

OPTIMISTIC_FORCE_INCREMENT

버전을 물리적으로는 변경되지 않지만, 논리적으로 변경된다.

엔티티를 수정하지 않아도 트랜잭션을 커밋할 때 update 쿼리를 사용해서 버전 정보를 강제로 증가시킨다.

데이터를 수정하지 않아도 트랜잭션을 커밋할 때 버전 정보가 증가한다.

 

 

JPA 비관적 락

데이터베이스 트랜잭션 락 메커니즘에 의존하는 방법이다.

  • 스칼라 타입을 조회할 때도 사용할 수 있다.
  • 데이터를 수정하는 즉시 트랜잭션 충돌을 감지할 수 있다.

 

발생할 수 있는 예외는 다음과 같다.

  • PessimisticLockException (JPA 예외)
  • PessimisticLockingFailureException (스프링 예외 추상화)

 

PESSIMISTIC_WRITE

비관적 락의 기본 디폴트 값이다.

 

PESSIMISTIC_READ

반복 읽기만 한다.

 

PESSIMISTIC_FORCE_INCREMENT

비관적 락중 유일하게 버전 정보를 사용한다.

 

 

2차 캐시

1차 캐시 : 영속성 컨텍스트 내부에서 저장하는 보관소

2차 캐시 : 애플리케이션 범위의 캐시를 지원하며 공유 캐시 , 2차 캐시라 한다.

캐시를 통해 조회 성능을 향상할 수 있다.

 

1차 캐시

객체 동일성(==)을 보장하며, 영속성 컨텍스트 범위의 캐시다.

 

2차 캐시

2차 캐시는 동시성을 극대화하려고 캐시한 객체를 직접 반환하지 않고 복사본을 만들어서 반환한다.

  • 2차 캐시는 영속성 유닛 범위의 캐시다.
  • 2차 캐시는 조회한 객체를 그대로 반환하는 것이 아니라 복사본을 만들어서 반환한다.
  • 2차 캐시는 데이터베이스 기본 키를 기준으로 캐시하지만 영속성 컨텍스트가 다르면 객체 동일성(==)을 보장하지 않는다.

 

 

@Cacheable
@Entity
public class Member {
	...
}

 

 

캐시 모드 설정
캐시 조회, 저장 방식 설정
JPA 캐시 관리 API 인터페이스의 다양한 옵션들이 존재한다.

 

 

하이버네이트와 EHCACHE 적용

  • 엔티티 개시
  • 컬렉션 개시 : 컬렉션이 엔티티를 담고 있으면 식별자 값만 캐시
  • 쿼리 개시 : 쿼리와 파라미터 정보를 키로 캐시한다. 결과가 엔티티면 식별자 값만 캐시한다.

hibernate-echache 라이브러리를 pom.xml에 추가해야 한다.

 

쿼리 캐시와 컬렉션 캐시의 주의점

캐시와 컬렉션 개시는 결과 집합의 식별자 값만 캐시한다.

따라서 쿼리 캐시나 컬렉션 캐시를 사용하면 결과 대상 엔티티에는 꼭 엔티티 캐시를 적용해야 한다.