[Spring boot] 트랜잭션과 락, 2차 캐시

어정윤·2021년 8월 20일
0

Spring boot 스터디

목록 보기
14/15
post-thumbnail

[Spring boot] 트랜잭션과 락, 2차 캐시

트랜잭션과 락

트랜잭션과 격리 수준

트랜잭션은 다음을 보장해야 한다.

  • 원자성(Atomicity) : 트랜잭션 내에서 실행한 작업들은 모두 성공하든가 모두 실패해야 한다.
  • 일관성(Consistency) : 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 한다.
  • 격리성(Isolation) : 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다.
  • 지속성(Durability) : 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 한다.

트랜잭션 간에 격리성을 완벽히 보장하려면 트랜잭션을 거의 차례대로 실행해야 한다. 이는 동시성 처리 성능이 매우 나빠지게 하므로 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의한다.

  • READ UNCOMMITTED(커밋되지 않은 읽기) - 격리 수준 가장 낮음
  • READ COMMITTED(커밋된 읽기)
  • REPEATABLE READ(반복 가능한 읽기)
  • SERIALIZABLE(직렬화 가능) - 격리 수준 가장 높음

위에서부터 아래로 갈수록 격리 수준이 노아지며, 격리 수준이 낮을수록 동시성은 증가하지만 격리 수준에 따라 다음과 같이 다양한 문제가 발생할 수 있다.

  • DIRTY READ
  • NON-REPEATABLE READ
  • PHANTOM READ

낙관적 락과 비관적 락

낙관적 락은 트랜잭션 대부분은 충돌이 발생하지 않는다고 가정하는 방법이다. 이것은 데이터베이스가 제공하는 락 기능을 사용하는 것이 아니라 JPA가 제공하는 버전 관리 기능을 사용한다.

낙관적 락은 트랜잭션을 커밋하기 전까지는 트랜잭션의 충돌을 알 수 없다.

비관적 락은 트랜잭션의 충돌이 발생한다고 가정하고 우선 락을 거는 방법이다. 이것은 데이터베이스가 제공하는 락 기능을 사용한다.

+ 두 번의 갱신 분실 문제(second lost updates problem) : 먼저 완료한 수정사항이 사라지고 나중에 완료한 수정사항만 남게 되는 문제
이를 해결하기 위한 방법은 3가지가 있다.

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

JPA 락 사용

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

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

JPA 사용 시 추천 전략은 READ COMMITTED 트랜잭션 격리 수준 + 낙관적 버전 관리다. -> 두 번의 갱신 내역 분실 문제 예방

LockModeType 속성

비관적 락과 타임아웃

비관적 락을 사용하면 락을 획득할 때까지 트랜잭션이 대기한다. 무한정 기다릴 수 없으므로 타임아웃 시간을 줄 수 있다.

타임아웃은 데이터베이스 특성에 따라 동작하지 않을 수 있다.

2차 캐시

1차 캐시와 2차 캐시

영속성 컨텍스트 내부에는 엔터티를 보관하는 저장소가 있는데 이것을 1차 캐시라 한다. 일반적인 웹 애플리케이션 환경은 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다.

하이버네이트를 포함한 대부분의 JPA 구현체들은 애플리케이션 범위의 캐시를 지원하는데 이것을 공유 캐시 또는 2차 캐시라 한다.

1차 캐시

  • 1차 캐시는 같은 엔터티가 있으면 해당 엔터티를 그대로 반환한다. 따라서 1차 캐시는 객체 동일성(a == b)을 보장한다.
  • 1차 캐시는 기본적으로 영속성 컨텍스트 범위의 캐시다(컨테이너 환경에서는 트랜잭션 범위의 캐시, OSIV를 적용하면 요청 범위의 캐시다).

2차 캐시

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

0개의 댓글