-
캐시는 서버의 부담을 줄이고, 성능을 높이기 위해 사용되는 기술이다.
- 예를 들어 어떤 요청을 처리하는데 계산이 복잡하거나 혹은 DB에서 조회하는게 오래 걸리는 등에 적용하여 결과를 저장해두고 가져옴으로써 빠르게 처리할 수 있다.
- 캐시는 값을 저장해두고 불러오기 때문에 반복적으로 동일한 결과를 반환하는 경우에 용이하다.
- 만약 매번 다른 결과를 돌려줘야 하는 상황에 캐시를 적용한다면 오히려 성능이 떨어지게 된다.
- 오히려 캐시에 저장하거나 캐시를 확인하는 작업 때문에 부하가 생기기 때문이다.
- 그러므로 캐시는 동일한 결과를 반환하는 반복적인 작업과 시간이 오래 걸려서 서버(애플리케이션)에 부담이 되는 경우에 적용하면 좋다.
-
스프링은 AOP 방식으로 편리하게 메소드에 캐시를 적용하는 기능을 제공하고 있다.
-
스프링에서 제공하는 기능을 활용해서 캐시를 적용하고 적용 전/후 성능을 비교해보자
Spring에서 캐시 적용하기
- 스프링에서는
@Cacheable
,@CachePut
,@CacheEvict
를 활용해서 캐시를 적용할 수 있다.
- @Cacheable과 같은 어노테이션 기반의 캐시 기능을 사용하기 위해서
@EnableCaching
를 추가한다.
@EnableCaching
@Configuration
public class RedisConfig {
...
}
- 캐시를 관리해줄 CacheManager를 Bean으로 등록해주어야 한다.
- 스프링 공식 문서 - 캐시 추상화 (Cache Abstarction) 에서 현재 지원되는 다양한 매니저의 종류를 확인할 수 있다.
-
캐시를 저장/조회를 설정한다.
-
보통 메소드에 적용한다.
- 클래스나 인터페이스에 지정할 수도 있지만 그런 경우는 극히 적다.
-
캐시에 데이터가 없을 경우에는 기존의 로직을 실행한 후에 캐시에 데이터를 추가한다.
-
캐시에 데이터가 있으면 캐시의 데이터를 반환한다.
@Cacheable("todayWebtoon")
public Webtoon getTodayWebtoon(String webtoonNo) {
// logic
}
-
파라미터로 넘어온
webtoonId
를 기준으로todayWebtoon
캐시에서 값을 조회한다.- 값이 없으면 logic을 실행하고, 반환 값을 저장한다.
- 값이 있으면 저장된 값을 반환한다.
-
만약 파라미터가 없는 경우 디폴트 값을 key로 활용하면 된다.
@Cacheable(value = "todayWebtoon", key = "webtoonNo")
public Webtoon getTodayWebtoon(String webtoonNo, Date date) {
// logic
}
- 파라미터가 여러 개인 경우 key를 지정해준다.
@Cacheable(value = "todayWebtoon", key = "#webtoon.webtoonNo")
public Webtoon getTodayWebtoon(Webtoon webtoon, Date date) {
// logic
}
- Key값의 지정에는 SpEL이 사용된다. 그렇기 때문에 만약 파라미터가 객체라면 다음과 같이 하위 속성에 접근하면 된다.
- 캐시에 값을 저장하는 용도로만 사용된다.
- 실행 결과를 캐시에 저장하지만, 조회 시에 저장된 캐시의 내용을 사용하지는 않고 항상 메소드의 로직을 실행한다.
@CachePut(value = "todayWebtoon", key = "webtoonNo")
public Webtoon updateWebtoon(String webtoonNo) {
// logic
}
- 캐시를 제거하는 용도로 사용된다.
- 만약 값이 달라지거나 현재 저장된 값이 무의미한 값이라면 제거되어야 한다.
- 캐시를 제거하기 위해서는 크게 두 가지 방법이 존재한다.
- 일정한 주기로 캐시를 제거
- 값이 변할 때 캐시를 제거
- 예를 들어 하루에 한 번씩 바뀌는 정보라면 batch 또는 scheduled 기능을 이용하여 일정 시간 주기로 캐시를 삭제한다.
@CacheEvict(value = "todayWebtoon", allEntries = true)
public void clearTodayWebtoon() {
// logic
}
@CacheEvict(value = "todayWebtoon", key = "webtoon.webtoonNo")
public void clearWebtoon(Webtoon webtoon) {
// logic
}
- 오늘의 웹툰을 조회하는 경우를 가정하고, 캐시를 적용했을 때와 적용하지 않았을 때의 성능을 비교한다.
- 두 가지 상황에 대해서 각각의 TPS를 측정하고, 추가로 평균, 최소, 최대 응답시간을 확인한다.
- 애플리케이션, DB, Cache 모두 로컬에서 작동한다.
- 웹툰 정보를 저장하는 관계형 데이터베이스는 MySQL, Cache는 Redis를 사용한다.
- 두 가지 상황(캐시 적용/미적용)에 대한 API를 만들고 각각의 상황에 알맞게 API를 요청한다.
- 이후 nGrinder를 통해 TPS를 측정하고, JMeter를 이용해서 평균, 최소, 최대 응답시간을 측정한다.
- Agent : 1
- Vusers : 10 (Processes : 2, Threads : 5)
- Run Count : 5000
- Threads : 500
- Period : 1
- Count : 500
- 캐시를 적용했을 때 TPS로 보나 평균 응답시간으로 보나 확실히 DB를 반복해서 조회하는 것보다 캐시를 이용하는 것이 효율적이다.
- 자주 호출되면서 변경이 적은 메소드에 적용한다면 효과적으로 기능을 제공할 수 있다.
- 스프링의 경우 AOP 형태 및 애노테이션 기반으로 쉽게 캐시를 저장/조회/수정할 수 있기 때문에 기능에 따라 캐시를 적용하기에 알맞는 조건인 경우, 해당 경우에서 기능적으로 최적화가 필요한 경우 캐시를 적용하자.