[스프링부트] 1차 캐시와 2차 캐시

YTT.erica·2024년 6월 23일

캐시

데이터베이스나 외부 시스템에 자주 접근해야 하는 데이터를 임시로 저장하는 메모리 공간

효과

  • 데이터 접근 시간 감소 → 시스템 성능 향상

종류

  • 1차 캐시
  • 2차 캐시
  • 메인 메모리 캐시
  • 디스크 캐시

영속성 컨텍스트

영속성 컨텍스트란, JPA의 생명주기를 관리하는 저장소

  • 애플리케이션이 데이터베이스와 상호작용할 때 사용되는 임시 데이터 저장 공간 -> 가상의 DB 역할
  • EntityManagerFactory, EntityManager, EntityTransaction으로 영속성 컨텍스트에 접근, 저장/조회, 관리

1차 캐시

1차 캐시 이란, 영속성 컨텍스트 내부에 존재하는 엔티티 보관 저장소

엔티티 매니저로 조회하거나 변경하는 모든 엔티티는 1차 캐시에 저장

→ 트랜잭션을 커밋 or 플러시 호출 시 1차 캐시에 있는 엔티티의 변경 내역을 데이터베이스에 동기화

→ 트랜잭션을 시작하고 종료할 때까지만 1차 캐시 유효

1차 캐시의 동작 방식

  1. 쿼리 실행 전 캐시 확인:

    클라이언트가 쿼리를 실행할 때, 데이터베이스는 먼저 JPQ 1차 캐시에서 해당 쿼리 결과가 이미 저장되어 있는지 확인

  2. 캐시 적중 시 응답 반환:

    쿼리 결과가 캐시에 저장되어 있는 경우, 데이터베이스는 즉시 캐시된 결과를 클라이언트에게 반환

  3. 캐시 미스 시 데이터베이스 쿼리 실행:

    쿼리 결과가 캐시에 없는 경우, 데이터베이스는 실제로 쿼리를 실행

  4. 캐시 업데이트:

    쿼리 결과를 얻은 후, 데이터베이스는 해당 결과를 JPQ 1차 캐시에 저장

1차 캐시 특징

  • 메모리 저장, 유효기간 짧음

1차 캐시 예시 - 조회

void cache() {
	EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();
	em.getTransaction().begin();

	Member member1 = em.find(Member.class, 1L);
	Member member2 = em.find(Member.class, 1L);

	System.out.println(member1 == member2);
	em.getTransaction().commit();
	em.close();
}

  • em.find(Member.class, 1L) 호출이 두 번 이루어짐 → 이미지 상 데이터베이스로 쿼리 실행
  • 두 번째 호출은 1차 캐시에서 엔티티를 조회하므로 데이터베이스에 접근 X

2차 캐시

2차 캐시 는, 애플리케이션이 공유하는 캐시 → 애플리케이션 범위의 캐시

2차 캐시는 애플리케이션을 종료할 때까지 캐시가 유지

2차 캐시의 동작 방식

  1. 1차 캐시 확인:

    클라이언트가 쿼리를 요청하면, 데이터베이스 시스템은 먼저 1차 캐시를 확인

    1차 캐시에 쿼리 결과가 있으면, 즉시 반환

  2. 2차 캐시 확인:

    1차 캐시에 결과가 없으면, 데이터베이스 시스템은 2차 캐시를 확인

    2차 캐시에 쿼리 결과가 있으면, 결과 반환 (객체가 아닌 복사본 반환)

    해당 결과를 1차 캐시에 저장

  3. 캐시 미스 시 데이터베이스 쿼리 실행:

    1차 캐시, 2차 캐시 모두 데이터가 없을 경우, 쿼리를 실행하여 결과 얻기

  4. 2차 캐시 및 1차 캐시 업데이트:

    쿼리 결과를 얻은 후, 결과를 1차, 2차 캐시에 모두 저장

2차 캐시 특징

  • 디스크 저장, 큰 용량, 메모리보단 느림
  • 장기 데이터 저장
  • 복사본을 1차 캐시에게 반환

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

→ 캐시한 객체를 그대로 반환 시, 여러 곳에서 같은 객체를 동시에 수정한 문제가 발생할 수 있음
→ 락을 걸면 동시성의 저하를 일으킴 → 따라서 복사본을 반환하여 동시에 수정

2차 캐시 사용법

  1. 캐시를 사용할 entity 클래스 위에 @Cacheable 어노테이션 작성

    @Cacheable // 기본 값 true. false로 지정하면 2차 캐시 x
    @Entity
    public class Member {
    
        @Id
        @GeneratedValue
        private Long id;
    
        ..
    }
  1. application.properties (application.xml) 에서 설정

    // 2차 캐시 활성화
    spring.jpa.properties.hibernate.cache.use_second_level_cache = true
    
    // 2차 캐시 처리할 클래스 지정
    spring.jpa.properties.hibernate.cache.region.factory_class
    
    // 하이버네이트로 캐시 적용 여부 확인 출력
    spring.jpa.properties.hibernate.generate_statistics = true
    
    // 캐시 모드 설정
    spring.jpa.properties.javax.persistence.sharedCache.mode= (기본값) Enable_selective

    캐시 모드 설정

    spring.jpa.properties.javax.persistence.sharedCache.mode= ?

    • ALL: 모든 엔티티 캐시
    • NONE: 캐시 사용 x
    • ENABLE_SELECTIVE: Cacheable(true)로 설정된 엔티티만 캐시 적용 → 기본 값
    • DISABLE_SELECTIVE: 모든 엔티티를 캐시하나, Cacheable만 캐시 적용 x
    • UNSPECIFIED: JPA 구현체가 정의한 설정 따름
  2. @Cache에서 캐시 동시성 전략 설정

    Cacheable
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    @Entity
    public class Academy {
    
        @Id 
        @GeneratedValue
        private Long id;
        
        private String name;
    	
        ...

    CacheConcurrencyStrategy.?

    • READ_ONLY: 자주 조회하고 수정 작업을 하지 않을 때
    • READ_WRITE: 조회 및 수정 작업할 때 → Phantom Read 발생 가능
    • NONSTRICT_READ_WRITE: 수정 작업을 거의 하지 않을 때 적합

1차 캐시와 2차 캐시

  • 공통점
    데이터베이스 접근 횟수를 줄이고 애플리케이션 성능을 최적하는데 목적
    모두 엔티티 인스턴스를 저장
  • 차이점
    1차 캐시2차 캐시
    범위인스턴스 범위애플리케이션 범위
    유효 범위트랜잭션 동안만 유효애플리케이션이 실행되는 동안 유효
    동작 방식엔티티를 영속성 컨텍스트 내부에 캐싱
    → 동일한 엔티티 매니저 내에서 동일한 엔티티를 조회할 때 동작엔티티를 영속성 컨텍스트 외부에 캐싱
    → 여러 트랜잭션 및 여러 엔티티 매니저 인스턴스에서 공유

참고자료

profile
'◡'✿ 꿈을 찾아가보자고~ '◡'✿

0개의 댓글