-
[스프링] @CacheableSpring 2020. 9. 15. 18:52
캐싱 ?
"오랜 시간이 걸리는 작업의 결과를 빠르게 접근 할 수 있는 곳에 저장하여 시간과 비용을 회피하는 방법"
>> 데이터나 값을 미리 복사해 놓는 임시 장소
: 스프링에서 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 캐싱 인터페이스를 구현
'Spring' 카테고리의 다른 글
[스프링] 시큐리티 -4 (SecurityContextHolder) (0) 2020.10.09 [스프링] 시큐리티 -3 (Test) (0) 2020.10.09 [스프링] 시큐리티 - 2 (DB 연동) (0) 2020.10.05 [스프링] 시큐리티 - 1 (인메모리 유저) (0) 2020.10.05 [스프링배치] 개념 (0) 2020.06.14 댓글