ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링] @Cacheable
    Spring 2020. 9. 15. 18:52

     

    캐싱 ? 

    "오랜 시간이 걸리는 작업의 결과를 빠르게 접근 할 수 있는 곳에 저장하여 시간과 비용을 회피하는 방법"

    >> 데이터나 값을 미리 복사해 놓는 임시 장소

     

    Spring Cache Tutorial

    : 스프링에서 Cache Annotation을 지원하여 좀 더 쉽게 캐시를 사용 할 수 있다.

     

    -> Spring AOP 

    -> AOP 특성, 동일한 클래스에서 메소드 호출은 AOP가 적용되지 않음. 즉 동일 클래스에서 캐싱이 적용된 메소드를 호출하면 적용되지 않는다.

     

    @EnableCaching 

    -> 어노테이션을 이용한 캐시기능을 사용하겠다 선언

     

    @Configuration
    @EnableCaching
    public class CacheConfig extends CachingConfigureSupport {
    
    
    	@Bean //사용할 캐시 매니저
        @Override
        public CacheManager cacheManager(){
        	// configure and return CacheManager instance
        }
        
        @Bean //캐시 키에 대한 결과를 돌려주는 Resolver
        @Override
        public CacheReslver cacheResolver()
        
        	//configure and return CacheResolver instance
        }
    
    	@Bean //특정 로직으로 캐시 키 생성
        @Override
        public KeyGenerator keyGenerator(){
        
        }
        
        @Bean
        @Override //에러 핸들러
        public CacheErrorHandler errorHandler(){
        
        
        }
    
    }

     

    @CacheAble

    -> 메소드 결과 값이 캐싱되고, 존재하는 키 요청은 캐시에서 찾아 반환

     

    [옵션]

    • key : 캐시 키
    • cacheNames : 캐시 이름 >> 캐시 그룹 
    • condition : 조건부 캐싱
    • sync : 캐싱 동기화 -> thread safe하지 않을 때 사용
    • unless  : 캐싱이 이루어지지 않은 조건

     

    @CacheEvict

    -> 메소드가 호출할 때 캐시에 저장되어있는 데이터 삭제

     

    [옵션]

    • allEntries : true / flase, 캐시 이름에 해당하는 값 모두 삭제

     

     

    스프링 캐싱 어노테이션 단점

    다건 처리를 할 수 없음, >> Mget, Mput 지원하지 않는다

     

    ex) In Query : select * from data d where d.id in ?1

    @Cacheable(cacheNames ="mget", key= "#p0")
    public List<Data> findAllById(List<Long> ids);

     

    • ids의 값은 리스트로 캐시 키로 설정 시, 해당 키의 값과 완전히 동일한 값이 아닌 이상 캐싱 사용이 어려움
      • ex ) ids = {1,2,3,4}  , ids = {1,2,4}, ids = {1,2,3}, ids = {4,3,2,1} >>모두 다른 키로 잡히며 동일한 값이 여러개 저장되면서 비효율적인 저장공간을 사용

     

     

    해결 방법 ?

     

    1. 모든 값에 대하여 캐싱하여, 코드내에서 처리

     

    ex) findAll : select * from data

    @Component
    class CacheTest{
    
      @Cacheable(cacheNames ="mget", key= "all")
      public List<Data> findAll();
    
    }
    
    @Test
    public test(){
    
      cacheTest.findAll();
      
      //찾은 데이터 중 필요한 것만 사용...
    
    }

     

    문제점 ?

    •  LocalCache가 아닌 GlobalCache 사용 시 불 필요한 네트워크 비용 발생
    • findAll로 찾은 데이터가 무수히 많으면 메모리 등 많은 리소스를 잡아 먹는다.

     

    2. 직접 구현 >> MultiGet / MultiSet

     

    ex)

    @Component
    class CacheTest{
      
      
      private final DbRepository dbRepository;
      private final CacheRepository cacheRepository;
    
      public List<Data> findAllById(List<Long> ids){
      
         
         
         //find Cache
         Map<Key, Value> cacheData = cacheRepository.mget(ids);
         
         //return value
         List<Value> returnValues = toList(cacheData);
         
        //find 캐시에서 찾지 못한 아이디 리스트
         Set<Long> notLoadIds = findNotLoadIds(cacheData);
         
         //캐시에서 찾지 못한 값이 있을 경우..
         if(CollectionUtils.isNotEmpty(notLoadIds){
             
            //DB에서 캐시에서 찾지못한 값을 찾는다.
            List<Data> notLoadValues = dbRepository.findByIds(notLoadIds)
            
           //Key, Value로 변경
           Map<Key,Value> notLoadKeyValues = toMap(notLoadValues);
           
           //캐시에 저장
           cacheRepository.mset(notLoadKeyValues);
            
           //머지 >> 리턴 Value는 모든것이 포함되어야함 
            merge(returnValues, notLoadKeyValues);
         }
         
         
         return returnValues;
      
      }
    
    }
    
    

     

    문제점 ?

    •  캐시 적용 시점에 따라 구현을 따로 해줘야 한다. >> 번거롭다

     

    해결 ? 

    MultiGet / MultiSet 캐싱 인터페이스를 구현 

    댓글

Designed by Tistory.