Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 티스토리챌린지
- 달인막창
- JanusWebRTCServer
- 자원부족
- 개성국밥
- pytest
- JanusGateway
- 오블완
- JanusWebRTCGateway
- tolerated
- table not found
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- python
- terminal
- mp4fpsmod
- preemption #
- addhooks
- PersistenceContext
- taint
- Value too long for column
- 깡돼후
- kotlin
- Spring Batch
- JanusWebRTC
- PytestPluginManager
- 코루틴 컨텍스트
- VARCHAR (1)
- 코루틴 빌더
- 겨울 부산
- vfr video
Archives
너와 나의 스토리
[JPA] Hibernate Cache란? / Hibernate cache로 인한 문제 해결 방법 본문
반응형
Hibernates를 ORM 프레임워크로 사용하는 애플리케이션의 성능을 향상하기 위해 자주 접근하는 데이터를 메모리에 저장한다. Hibernates는 두 가지 수준의 캐시를 지원한다.
* ORM(Object Relational Mapping): 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것
1차 캐시(First-Level Cahce) = 세션 캐시(Session Cache)
- Hibernate는 세션 수준의 캐시를 사용한다. 특정 세션에서 현재 사용 중인 데이터를 저장하고 있다.
- 즉, persistent 상태인 모든 객체를 캐싱함.
- 영속성 컨텍스트(Persistence Context)에 데이터 저장.
- 영속성 컨텍스트: 엔티티 객체의 상태를 추적하고, 디비와의 상호작용을 관리하는 역할.
- 세션에서 어떤 엔티티를 첫 번째로 불러오거나 업데이트하면 세션 수준 캐시(1차 캐시)에 저장된다.
- 같은 세션에서 같은 엔티티에 가져오는 후속 요청이 들어오면 캐시에서 데이터를 반환해 줍니다.
- 세션 수준 캐시는 기본적으로 활성화되어 있으며, 비활성화시킬 수 없다.
2차 캐시(Second-Level Cache) = 쿼리 캐시(Query Cache)
- Hibernate는 여러 세션 간에 공유되는 캐시를 가진다.
- 동일한 쿼리를 실행할 때 캐싱한 값을 반환
Hibernate cache로 인한 문제 발생
ZuulFilter를 쓰다가 Spring Cloud Gateway로 넘어오면서 ProxyExchange를 사용해 기존 Zuul filter를 대체하였다.
public ResponseEntity<byte[]> proxy(ProxyExchange<byte[]> exchange, JsonNode body) {
saveDataA();
return exchange.uri(routingUrl).post(responseEntity -> {
findDataA();
return responseEntity;
});
}
- 위 예시 코드A라는 데이터를 저장(1)하고 routing한 후, 라우팅한 서버로부터 응답이 오면 A라는 데이터를 다시 디비에서 조회해서(2) 처리하는 로직이다.
- 이때, (1)번 작업 후 (2)번 작업이 수행되기 전에 다른 api 콜이 들어와 updateDataA()를 수행한 경우 문제가 발생하였다.
- 중간에 updateDataA()를 호출되었으면 (3)에서 A를 조회할 때, 업데이트된 값이 반환되어야 하는데 처음 저장한 A 값 그대로 반환되었다.
- 원인: 위 코드는 모두 한 세션에서 작업이 이루어지기 때문에 1차 캐시에 저장된 값이 반환되는 것이었다.
- 조회 쿼리는 처음 한번만 db에 접근하며, 그다음부터는 캐시 값만 사용함.
- 해결 방법 1: 데이터를 조회할 때, cache 업데이트
- entityManager.clear()을 하면 영속성 컨텍스트의 모든 엔티티를 제거할 수 있다.
- 이로 인해 영속성 컨텍스트가 관리하던 모든 엔티티의 상태가 비영속 상태로 변경되어 현재 변경 사항이 모두 롤백된다.
- 아래 예제처럼 entityManager.refresh(entity)를 호출하면 특정 엔티티를 데이터베이스로부터 다시 로드하여 영속성 컨텍스트의 상태를 업데이트할 수 있다.
- entityManager.detach(entity)를 호출하면 entity를 영속성 컨텍스트에서 분리시킬 수 있다.
- 분리된 엔티티는 영속성 컨텍스트의 관리를 받지 않게 된다. 1차 캐시에서도 제거된다.
- 하지만, 이후에 해당 엔티티를 수정하거나 업데이트하려면 entityManager.merge(entity)를 사용해야 한다.
- entityManager.merge(entity)를 호출하면 해당 엔티티가 다시 영속성 컨텍스트에 병합된다.
- entityManager.clear()을 하면 영속성 컨텍스트의 모든 엔티티를 제거할 수 있다.
@PersistenceContext
private EntityManager entityManager;
@Transactional
public DataA find(DataAId dataAId) {
val entity = repository.findById(dataAId.getValue())
.orElseThrow(() -> new DataANotFoundException(dataAId));
entityManager.refresh(entity);
return entity.asModel();
}
- 해결 방법 2: 영속성 컨텍스트 생존 범위 지정
- Spring은 요청이 시작될 때 새로운 Hibernate 세션을 연다. 세션이 반드시 데이터베이스에 연결되어 있지는 않는다.
- open-in-view 옵션이 true인 경우, 영속성 컨텍스트가 요청의시작부터 응답의 종료까지 유지된다. 즉, 영속성 컨텍스트가 틔랜잭션 범위를 벗어나도 유지된다. (기본적으로 이렇게 설정되어 있음)
- 이 옵션을 false로 설정하면, 영속성 컨텍스트는 데이터베이스 트랜잭션 범위 내에서만 유지되게 된다.
- 설정 방법:
<application.yml>
spring:
jpa:
open-in-view: false // true가 디폴트
위 로직을 Zuul Filter로 구현했을 때에는 문제가 발생하지 않았던 이유
- 스프링 필터의 경우 각각의 필터가 하나의 독립적인 요청으로 인식되어 세션이 각각 생성된다.
- 즉, 각각 다른 영속성 컨텍스트를 가지므로 이러한 문제가 발생하지 않았던 것.
출처
- Hibernate 5.5 Documentation: Caching
반응형
'개발' 카테고리의 다른 글
Appium 1.x에서 2.x로 변경하기 - 주요 새 기능, 변경 사항 정리 (0) | 2023.08.11 |
---|---|
[리액티브 프로그래밍] - Part 1: 리액티브 프로그래밍이란? Reactive Programming (0) | 2023.06.25 |
TDD, 클린 코드 with Java 16기 수강 후기 & 피드백 정리 (0) | 2023.04.08 |
Indexing이란? 인덱싱하는 이유 장점/단점, ID로 인덱싱하는 게 더 효율적인가? (0) | 2023.02.26 |
Create Video from VFR images with timestamp / mp4fpsmod 사용 방법 (0) | 2023.01.12 |
Comments