일반적으로 자바 웹 서비스는 cache(oscache나 ehcache)를 이용하여 성능을 높인다. 이 cache를 적용하면 메모리에 캐쉬 데이터가 쌓이게 된다.

 

* 일반적인 경우

jvm의 memory 관리 방식이 Generational Model로 설정되어 있는 경우, Young Gen이 조금씩 커지기 시작하고, Young Gen과 Survivor Gen 간의 여러 번의 작은 gc가 일어나서 old gen으로 승격이 일어나게 된다.

이런 old gen에서의 Full GC는 결국 부하를 일으키는 요인이 되고, Full GC 시간이 길어지면 그 시점에 접속한 웹 서비스들은 느려지거나 장애로 이어지게 된다.

이를 해결하기 위해서 일반 개발자들은 CMS 알고리즘으로 완벽히 무장하거나, Survivor ratio를 잘 조절하여 전체 Young Gen 크기를 넓히는 방법을 사용하기도 한다. (개인적으로는 후자를 선호)

 

 

그러나 사실 곰곰히 생각해보면..

Young Gen 안의 데이터가 최대한 쌓이지 않도록 조절해서 Young Gen안에서 자연스럽게 GC(minor gc)가 일어날 때, 자연스럽게 정리되는 것이 가장 좋다.

이 때 오랫동안 data가 저장되는 것은 DB (mybatis cache) 나 특정 주기에 가져오는 데이터들이다.

 

이 cache data들만 잘 분산한다면. 효율적이 될 수도 있겠다 생각이 들어서.. 특이한 실험을 했다.

하나의 물리장비에 단일 JVM과 단일 Cache Daemon (redis 또는 memcached) 을 사용했다. 참고로 cache hit는 70% 정도 되었다.

 

 

ehcache나 oscache에 비해서 io 부하(tcp/ip)가 있다는 부담감이 있었지만. 사용해보니 만족스러웠다.

물론 성능쪽 이득도 있지만, 가장 좋은 것은 jvm의 메모리 사용량이었다.

 

Spring Cache를 이용해서 ehcache binding 했을 때는 이런 식의 Heap Graph를 볼 수 있었다.

 

 

 

그러나 cache daemon을 따로 떼어내니. 아래와 같은 깔끔한 Heap Graph 를 볼 수 있었다. 작은 용량으로 간단한 minor gc가 일어날 뿐이었다. minor gc는 5ms 이내이다.

 

 

 

jvm 메모리 관리를 하지 않아도 되니. 완전 마음 부담이 덜해지고, Full GC는 전혀 일어나지 않았다.

다만. cache 크기를 확인해 보는 것이 중요하긴 하지만, 캐쉬 용량 대비해서 전체 max memroy 크게 잡은지라. 큰 이슈는 없는 듯 하다.



주의할 점은 로컬 redis에 다른 서버들이 접근하지 않도록 redis.conf를 수정할 필요가 있다. 아래와 같이 수정하고 redis.conf를 읽도록 하면 웹 애플리케이션만 로컬 redis 를 접근할 수 있다.

bind 127.0.0.1


Posted by 김용환 '김용환'

댓글을 달아 주세요