적용범위 검토기간 - 2019. 11. 07(목) ~ 2019. 11. 11(월)
작업 기간 - 2019. 11.11(월) ~ 2019. 11. 15(금)
우선 배경은 이러했다.
11월 커머스의 마케팅이 몰리는 시기에 카카오톡 알림톡이 80만명에게 나갈 예정이였다.
평소 50만명에게 전송을해 동접자가 늘거나 '에어팟' 이벤트(선착순 구매) ( 참. .사람들은 선착순을 좋아해. .나도 그렇지만)를
하게 되면 동접자가 늘었을때 서버가 버티지 못해서 서비스에 지장을 끼치고 있었다.
이러한 문제를 해결해야 했고, 물들어올때 노저어야 하는 것처럼 동접자를 늘리기 위한 작업을 해야했다.
우리는 REDIS 서버를 구축해 놓았었고, 웹서비스는 JEDIS로 적용되어 있었다.
(시간이 있으면 RedisOn으로 변경할 예정)
사용되는 Keys PC, MC 합쳐서 10개 뿐이였고. 동접자가 증가하였을때 DB 부하가 커져 서비스에 문제가 생기고 있었다.
(물론 생성되는 Key는 많았다. 생성되는 템플릿이 10개인거다. service 메서드에 붙여놓은 @Cacheable key생성이 10개)
당시 WAS는 많이 구성되었고 DB서버는 4대로 구성되어있었다.
그러다보니 동접자가 3만명 이상이되면 DB 로드밸런싱이 제대로 되지못했고 어떤날은 한 DB서버의 CPU사용량이 90%가 넘어가기도 했었다.
DB 부하에 가장큰 영향을 준것은 상품의 가격과 재고계산 관련 로직이 큰부하를 줬고.
해당 DB를 Cache로 가지고 간다면 DB부하가 줄어 동접자가 늘 것이라는 가설을 세웠다.
그러므로 나는 REDIS를 활용해서 DB부하 부터 줄여보자였다.
(추후에는 소스 리팩토링을 해야겠다 라고 생각을 했고.. 지금 이시점에는 1차 리팩토링은 완료했다.
따로 글을 남기지 않을거 같아서 Android 기준으로 랜딩속도가 40%정도 줄었다 .정말 놀라운 성과였다.)
결과는 성공적이였고 고객이 자주 조회하는 상품, 가격, 재고 Data를 Redis에 담아 부하를 절감하였다.
생성되는 key는 총 38개로 확대적용하였다.
물론 cacheName을 분리하여 expire 주기는 다르며, 각 알맞는 시간을 찾아보았다.
아래는 Redis 적용시 필요한 소스들만 넣어보았다.
cacheContext.xml
<cache:annotation-driven cache-manager="cacheManager" />
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg ref="cacheRedisTemplate" />
<property name="usePrefix" value="false" />
<property name="defaultExpiration" value="300" /> <!-- 5 min -->
<property name="expires">
<map>
<entry key="dev-jj" value="#{properties.getString('cache.expire.dev-jj')}" /> <!-- 아주 오래 걸어도 관계없음 -->
<entry key="dev-jjProd" value="#{properties.getString('cache.expire.dev-jjProd')}" />
<entry key="dev-jjDeal" value="#{properties.getString('cache.expire.dev-jjDeal')}" />
</map>
</property>
<property name="cacheNames">
<list>
<value>dev-jj</value>
<value>dev-jjProd</value>
<value>pdev-jjDeal</value>
</list>
</property>
</bean>
Service.java
import org.springframework.cache.annotation.Cacheable;
@Cacheable(value = "dev-jjProd", key = "#this.target.getRemoteCachePrefix().concat(#root.methodName).concat(#params['SQ'])", condition="#params['isCache'].equals('true')")
public List<LSMap> getList(LSMap params) throws ServiceException {
return Dao.getList(params);
}
@Cacheable(value = "dev-jjDeal", key = "#this.target.getRemoteCachePrefix().concat(#root.methodName).concat(#params['SQ'])", condition="#params['isCache'].equals('true')")
성과
동접자는 기본 3만명 이상 수용가능하며 5만명까지도 가능할 것으로 예상된다.
DB서버의 부하를 DB튜닝 없이 줄일 수 있는 크리티컬한 방안이기도 하다.
사실 Redis와 같은 Cache DB는 고객에게 자주 노출되는 서비스일 수록 조건이 아닌 필수라고 생각한다.
'Delvelopment > Redis' 카테고리의 다른 글
[REDIS] Sorted Set 명령어 정리 (0) | 2021.08.06 |
---|---|
캐시(Cache) Local Cache & Global Cache (0) | 2021.03.11 |
Redis의 자료형, 특징과 시간복잡도 그리고 클러스터 (0) | 2021.03.10 |
REDIS Key Evict 하는 두가지 방법 (키 삭제, 키 초기화) (0) | 2020.03.13 |
REDIS 명령어 (keys, Scan, get, flushall, Del 등) (0) | 2020.03.13 |
댓글