본문 바로가기
개발 관련 강의 정리/10분 테코톡

[10분 테코톡] 썬의 캐싱 정리

by 코딩개발 2023. 6. 4.
728x90
반응형


위와같이 프로세서의 성능이 매년 60%씩 증가하는 반면에 메모리 지연 시간 향상은 9%밖에 일어나지 않는다. 즉, 각 성능 간의 격차가 매년 50%씩 증가하여 아무리 프로세서가 성능이 좋아져도 메모리 처리 속도가 느리기 때문에 전체적인 프로그램 속도가 느리게 된다. 따라서 캐시라는 것이 등장하였다.

 

 

캐시(Cache)란?


사전적 정의
- 물건을 일시적으로 저장하거나 보관하기 위해 사용하는 곳

컴퓨터 과학에서의 정의
- 캐시는 데이터나 값을 미리 복사해 놓는 임시 장소
- 미리 복사하여 데이터에 더 빠른 속도로 접근 가능

 

 

메모리 구조

 

아래로 내려갈수록 용량은 커지지만 접근하는 데 드는 비용은 더 많아진다고 보면 되고 레지스터나 캐시같은 경우는 순식간에 접근이 가능한데 메인 메모리를 넘어서서 외부 장치까지 접근을 하려면 더 많은 명령주기가 실행되는 것을 확인할 수 있다.

 

 

캐싱의 종류

 

CPU 캐시 메모리

 

위에는 인텔 코어 i7 쿼드 코어 칩인데 각 코어마다 우선 L2 캐시가 들어 있는 걸 볼 수 있다. 그리고 각 코어들이 모두 다 같이 공유하는 Shared L3 캐시가 있다. 이렇게 메모리에서 캐시면적으로만 봐도 캐시가 얼마나 중요한지 알 수 있다. TMI로 오늘날 CPU칩의 캐시 면적은 약 30%에서 70%가 된다고 한다.

 

웹 캐시

 

웹 캐시는 클라이언트가 서버에게 요청을 하면 데이터를 응답받는 과정에서 어디에선가 캐싱을 통해서 서버 트래픽비용을 줄이고 접근시간을 줄이기 위해 캐싱을 사용한다. 여기에는 브라우저 캐시와 프록시 캐시, 그리고 게이트웨이 캐시가 존재한다.

 

브라우저 캐시

 

브라우저 캐시는 요청과 응답을 하는 과정 이후에 클라이언트 내부 디스크의 정적 파일들을 저장해 놓는 것인데 이를 통해 다음부터는 서버까지 요청을 할 필요 없고 내부 캐시로부터 불러올 수 있다.

 

 

캐싱(Caching)

- 캐싱은 이러한 캐시라는 작업을 하는 행위(행동)
- 자주 사용하는 데이터를 저장해서 재활용하는 기술

 


자바 코드를 작성할 때 Integer 클래스를 많이 볼텐데 Integer는 내부적으로 IntegerCache 라는 클래스를 가지고 있다. 빨간 박스를 살펴보면 cache 라는 배열에다 미리부터 low부터 high까지의 Integer 인스턴스를 생성해둔 뒤에 저장을 하고 있다.이후에 Integer 객체를 만들 때는 캐시로부터 가져오거나 만약 범주 내에 없다면 새로운 인스턴스를 생성 하는 것을 볼 수 있다.

 

 

Java 코드로 캐싱

 

Lotto 미션을 예로 알아보자. 요구사항은 아래와 같다.
- 로또 번호는 1부터 45 사이의 숫자여야 한다.
- 번호가 랜덤하게 생성되어야 한다.
- 번호가 중복되면 안된다.
- 번호가 정렬되어야 한다.
- 로또 한 장은 6개의 로또 번호로 이루어져야 한다.

 

 

이제 LottoNumber 라는 VO를 만들게 되는데 검증도 하고 LottoNumber를 여섯 자리로 만들어야 하니까 LottoNumbersGenerator라는 클래스를 따로 두고 여섯개의 숫자를 만들어주는데 이때 문제점이 LottoNumber 객체를 매번 생성 하고 있다는 것이다. 만약 천만장의 로또를 발급해야 한다고 치면 육천만개의 LottoNumber객체를 생성 해야한다.

 

 

이는 메모리 낭비와 직결되기 때문에 위와 같이 캐싱을 사용하면 된다. 캐시라는 맵을 상수로 둔 후에 클래스 로딩 시점에서 캐시를 초기화시킨다. 이때 초기화시키는 방법은 LottoNumber 객체를 1부터 45 까지 미리 생성을 해두고

 

 

이 LottoNumberGenerator에서 사용을 할 때면 매번 새로운 객체를 생성하는 것이 아니라 1부터 45에서 캐싱 해둔 값만 꺼내와서 재사용을 하게 되는 것이다.

 

 

Spring에서의 캐싱

 

캐시 추상화(Cache Abstraction)
- 스프링에서 빈의 메서드에 캐시를 적용할 수 있는 기능을 제공
- AOP를 이용해 메서드 내부 구현에 영향을 미치지 않고 적용
- 특정 캐시 기술에 종속 X
- 캐심이 필요한 비즈니스 로직에서 EhCache, Redis 등 캐싱 종류에 의존할 필요 X

 

캐시 매니저(Cache Manager)
캐시 추상화에서는 캐시 기술을 지원하는 캐시 매니저를 빈으로 등록해야 한다.

 

o ConcurrentMapCacheManager
    - 캐시 정보 Map 타입.
    - 빠르고 별다른 설정이 필요없음.

 

o SimpleCacheManager
    - 기본적으로 제공하는 캐시가 X
    - 사용할 캐시를 직접 등록하여 사용하기 위한 캐시 매니저

 

o EhCacheCacheManager
    -EhCache를 지원하는 캐시 매니저

 

이외에도 CaffeineCacheManager, CompositeCacheManager, JCacheCacheManager 등이 있음.

 

 

● Spring의 캐싱 기능을 사용하기 앞서, 필요한 설정


스프링부트를 사용한다면 위와같이 spring-boot-stater-cache를 build.gradle에 선언을 해주면 되고@Configuration 어노테이션을 붙인 클래스를 만들고 @EnableCaching을 해주면 된다. 이후에 캐시매니저를 등록해주어야 되는데 여기서는 테스트 용도로 사용하기 위해서 SimpleCacheManager를 만들었고, ConcurrentMapCache를 사용을 했는데 여기에 사용할 캐시 공간의 이름을 넣어주어야 하기 때문에 공간 이름을 movies로 지정하였다.

 


이후 서비스 코드에서 캐싱을 사용하지 않는 코드를 작성하고 캐시를 사용하는 코드를 작성해 보았는데, 캐시를 사용한다면 @Cacheable 어노테이션을 붙여주고 캐시 공간 이름을 지정을 해주면 된다. 이후에 delayQueryForTwoSeconds() 메서드를 두었는데 데이터베이스에 쿼리를 날릴 때 너무 많은 데이터가 온다면 약 2초 정도 걸릴 거라는 가정을 하고 쓰레드를 2초간 잠들게 하였다.


이후 컨트롤러에서 각각 세 번씩 호출해 보았는데 캐시를 사용하지 않는다면 매번 전체 메서드가 실행이 되기 때문에
쿼리가 날아가고 2초 이상씩 걸리는 것을 확인할 수 있다. 캐시를 사용한다면 첫번째는 캐시의 데이터가 없기 때문에 내부 메서드들을 먼저 다 실행하고 캐시에 저장을 한 후에는 캐시로부터 데이터를 가져오기 때문에 2밀리초와 0밀리초가 나온 것을 확인할 수 있다.

 

 

주의할 점

모든 상황에서 캐싱을 사용하는 것은 좋지 않다.
* 캐시는 값을 저장하고 불러오기 때문에 반복적으로 동일한 결과를 반환하는 경우에 용이
* 매번 다른 결과를 돌려줘야 하는 상황이라면 오히려 성능 저하를 야기
* 캐시 저장 및 확인 작업에서 부하가 생김
* 작업의 시간이 오래 걸리거나 서버에 부담을 주는 경우에 사용을 고려


참고

https://www.youtube.com/watch?v=H4J-8pPMvEU&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC 

728x90
반응형

댓글