0. 캐시 정의


캐싱에 대한 개념을 찾아보던 중 공식적인 용어 정의 말고 좀 더 알기 쉽게 정의한 것은 없을까 고민했었다.

내가 내린 결론은 다음과 같다.

캐싱은 성능상 이점을 얻기 위해 실시간성은 후순위에 두는 것

❓ 그렇다면 여기서 성능상 이점이란 어떤 이점을 말하는걸까?


1. 캐싱 기본 동작


이건 캐시의 동작을 살펴보면 이해하기 쉽다.

캐시의 동작 과정은 보통 다음과 같다.

  1. 데이터 요청
  2. 캐시에 있는지 확인
  3. 캐시에 있다면 캐시에서 바로 조회, 없다면 DB에서 데이터를 조회
  4. DB에서 조회해왔다면 해당 데이터를 캐시에 저장

즉, 성능적 이점은 메모리 기반의 캐시에서 데이터를 조회해 동일한 조회 요청에 대해 DB에 매번 접근하지 않는 것에서부터 온다는 뜻이다.


2. 캐시 히트 & 캐시 히트율


여기서부터 캐시 히트(Cache Hit)에 대한 개념이 들어간다.

우선 캐시 히트란 조회하려는 데이터가 캐시에 존재할 때를 말한다.

그렇다면 반대로 조회하려는 데이터가 캐시에 존재하지 않는다면?

그건 캐시 미스라고 한다.

그리고 이를 비율로 나타낸 캐시히트율이라는 개념이 존재하는데 이는 말 그대로 전체 조회 횟수에 대한 캐시 히트 비율을 말한다.

즉, 캐시 히트 / (캐시 히트 + 캐시 미스)를 말한다.

앞서 말했듯이 캐시는 조회 요청에 대한 성능적 이점을 얻으려고 사용하는 것이기 때문에 이 말은 결국 캐시 히트율을 높이는 것이 제일 중요하다는 뜻과 같다.

캐시 히트율을 높이려면 다음과 같은 상황이면 바람직하다.

  1. 동일한 입력에 대해서 항상 동일한 결과를 반환하는 데이터
  2. 자주 조회되나 수정은 잘 발생하지 않는 데이터

✅ 자 결론을 내려보자. 캐시는 동일한 조회에 대해 성능적 이점을 얻기 위한 기술이며 이 성능적 이점을 확실히 누리려면 캐시 히트율을 높이는 것이 중요하다. 캐시 히트율을 높이기 위해선 자주 조회되나 수정은 잘 발생하지 않는 데이터를 대상으로 캐싱 전략을 세우면 된다.


3. 캐싱 전략


이제 캐싱 전략에 대해 알아보자.

캐싱 전략엔 Local CachingGlobal Caching, 2가지 전략이 있다.

두 전략의 가장 큰 차이점은 캐시 데이터의 저장 장소이다.

우선 Local Caching은 로컬 서버가 내부 저장소에 캐시 데이터를 저장하는 자체적 소유 방식을 사용한다.

로컬 서버에서 바로 가져올 수 있기 때문에 속도 면에선 장점이 있다.

하지만, 단일 서버가 아닌 다중 서버라면 데이터 일관성 유지를 위한 동기화 과정이 발생하며 만약 동기화가 제대로 이루어지지 않는다면 데이터 일관성에 문제가 생긴다.

그에 반해 Global Caching은 로컬 서버와 분리된 별도 저장소에 데이터를 저장하는 방식이다.

그렇기 때문에 Local Caching에 비해 I/O 비용이 추가되어 속도가 느리다.

하지만, 장점은 확실히 보장되는데 다중 서버라고 할지라도 동일한 외부 저장소에서 데이터를 조회해오기 때문에 데이터 일관성 유지가 된다.


4. 캐싱 전략 선택의 기준


그렇다면 이제 고민이 된다.

❓ 어떤 캐싱 전략을 사용해야 하는 것인가?

정답은 없지만 앞서 각각의 특성을 고려한다면 속도데이터 일관성, 이 2가지 부분에서 전략이 갈린다고 생각한다.

가령, 금전이 오고 갈 수 있기에 정확한 정보가 반영되어야 하는 상품 데이터에 Local Cache 전략을 사용한다고 생각해보자.

데이터 일관성 유지가 제대로 되지 않거나 동기화 과정이 오래 걸린다면 심각한 문제가 발생할 수 있다.

반대로, 상품의 카테고리 정보나 사용자 프로필 조회와 같은 데이터 일관성 유지를 위한 동기화 과정이 오래걸려도 빠른 조회가 필요한 부분엔 Local Cache 전략을 사용하면 좋다.


5. Spring에서의 Cache


Spring Cache Manager에 보면 첫 문장에 다음과 같이 명시되어 있다.

Spring Framework provides support for transparently adding caching into an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.

Spring Framework가 Spring 응용 프로그램에 캐시를 투명하게 추가할 수 있도록 지원한다고 되어있으며 트랜잭션 지원과 유사하게 캐싱 추상화를 사용하면 코드에 미치는 영향을 최소화하면서 일관되게 캐싱 기능을 사용할 수 있다는 뜻이다.

그러면 구체적으로 어떻게 추상화를 하고 있는지는 36.2 Understanding the cache abstraction36.3.6 Enable caching annotations에 나와있다.

내용을 종합적으로 정리해보면, 캐시 추상화가 AOP 방식으로 적용되기 때문에 개발자는 기술에 종속받지 않고도 사용할 수 있다고 되어있다.

캐시 추상화는 캐시 매니저를 Bean으로 등록해야 가능한데, 대표적으로 스프링 캐시를 관리하는 인터페이스인 CacheManager가 바로 그것이다.

이 인터페이스를 구현한 ConcurrentMapCacheManager, SimpleCacheManager, EhCacheManager, RedisCacheManager등 다양한 구현체들은 결국 AOP를 통한 동일한 캐시 로직이 적용되기 때문에 동일한 캐시 기능을 사용할 수 있는 것이다.


✅ 종합하면, Spring에서의 캐싱 기능은 Spring Framework에서의 다른 기능과 마찬가지로 추상화되어 있다.

그렇기 때문에 캐시 로직에 대해선 직접 작성하지 않아도 되며 기술에 종속받지 않는다.

단, 구현체가 여러 개 존재하기 때문에 캐시 데이터를 저장할 스토리지 선정을 포함한 기술 선정과, 관련 설정을 넣어주는 작업은 반드시 필요하다.


6. 마무리


내용이 길었지만 하나씩 상기시켜 보겠다.

우선, 캐싱 개념과 캐싱을 사용하는 이유를 먼저 알아보았다.

그 다음으로, 캐싱을 이용해 성능적 이점을 누리기 위해선 캐시 히트율을 높여야 하며 캐시 히트율을 높이기 위한 조건들도 알아보았다.

다음으로, 이러한 캐싱을 위한 전략으로 Local CachingGlobal Caching에 대해 알아보았고, 실제 적용 시 기준에 대해서도 정리해보았다.

마지막으로, Spring에선 이러한 캐싱을 어떻게 제공하고 있는지까지 정리했다.

개발을 하면서 실제 개념을 아는 것도 중요하지만 적용은 또 다른 문제라는 것을 배웠다.

다음 게시글에선 실제 프로젝트에서 어떤 캐싱 전략을 사용하여 어떤 데이터 조회에 사용할 것인지를 다뤄보도록 하겠다.

References

Leave a comment