사용자 요청 서비스가 시작되어 종료될 때까지 유지되는 캐시이다. 서비스 요청이 들어와 어플리케이션 시작될 때 캐시가 초기화되어 비어 있고 어플리케이션이 종료될 때 캐시가 지워져 사라지는 구조로 사용자 서비스간에는 데이터 공유가 되지 않는다.
[특성]
- 사용자 서비스 요청 내에서만 유효한 캐시로 요청(Request)마다 자신만의 캐시를 가지는 구조
- 한 쓰레드에서만 액세스가 이루어지므로 동시성 제어를 위한 락(Lock)을 필요로 하지 않음
- 한 서비스 요청에서 사용하는 데이터만 캐시하고, 사용 후 지워지므로 메모리 사용량이 적음
- 캐시의 라이프사이클이 서비스 요청단위로 짧아서 DB와 데이터 동기화를 고려하지 않아도 됨
[구현 시 고려사항]
- 자바의 경우 캐시가 ThreadLocal이나 Service Context에 위치하도록 함
(TP 경우는 글로벌 변수로 선언함) - 서비스 시작과 종료 시에 캐시가 프레임워크 레벨에서 자동으로 Initialize/clear 되도록 함
- 한 요청 내에서 대량 건이 조회되거나 다양한 입력 값(데이터 키)으로 빈번하게 사용하여 Hit율이 낮은 형태에는 맞지 않음(즉 Request cache에 대량 데이터가 쌓일 가능성이 있으면 적용 못함)
[일반적인 적용 예]
- 고객 기본정보 캐시: 고객관리 시스템에서는 고객 기본정보 조회가 한 서비스 내에서 반복 호출되는 경우가 있는데 기존에 조회된 데이터 없으면 DB 조회하여 캐시 시킨 후 내보내고, 이미 조회된 데이터가 있으면 캐시된 데이터를 내보냄
- 보험사 환급요율정보 캐시: 해지환급금 예시표 같은 조회 프로그램은 년도별 환급금을 계산하기 위해 비슷한 조회가 수천 회까지 이루어지는데, 보험사 요율 테이블은 상품 종류에 따라 다양하여 전체 테이블 캐시는 불가능함. 그러나 한 상품의 환급요율은 데이터건수가 적으므로 서비스 요청 단위에서 캐시하여 사용하면 큰 성능개선 효과를 볼 수 있음
<적용코드 예>
--------------------------------------------------------------------------------------
1. Cache 모듈 작성
(ThreadLocal HashMap으로 만드는 것이 핵심 로직임)
package com.perf;
import java.util.HashMap;
public class RequestCache {
static final private ThreadLocal<HashMap> cache = new ThreadLocal<HashMap>();
static public void initialize(){ // Request 시작 시에 캐시 초기화
cache.set(new HashMap());
}
static public void remove(){ // Request 종료 시에 캐시 삭제
cache.set(null);
}
static HashMap get(String id){ // ID에 대한 캐시 get 수행(없으면 null 리턴)
HashMap map = cache.get(); // ID에 대한 개선 지정여부 확인 및 캐시 리턴
if(map == null)
return null;
return (HashMap)map.get(id);
}
static void set(String id){ // ID에 대해 캐시가 동작하도록 설정 수행
set (id, 10);
}
static void set(String id, int size){
HashMap map = cache.get();
if(map == null)
return;
if(!map.containsKey(id)){
map.put(id, new HashMap(size));
}
}
static void clear(String id){ // ID에 대한 캐시 제거
HashMap map = cache.get();
if(map == null)
return;
map.remove(id);
}
static void clearAll(){ // Request 내에 있는 모든 캐시 제거
HashMap map = cache.get();
if(map == null)
return;
map.clear();
}
}
------------------------------------------------------------
2. SQL 수행하는 Framework에 Cache 코드를 적용함
(아래코드는 이해를 위한 Sample 코드임-Cache 핵심 로직)
public LMultiData executeQuery(String sql_id, LData parameters) throws SQLException {
LMultiData return_data = null;
LMultiData cache_data = null;
String cache_key = null;
Map cache = RequestCache.get(sql_id); // 서비스 시작 부분에 RequestCache.set(sql_id)가
// 지정되었으면 캐시가 나오고, 아니면 null 리턴
if(cache != null){ // 캐시를 사용하도록 지정된 sql_id이면
cache_key = parameters.toString(); // 바인드 변수를 기반으로 캐시 키 생성
cache_data = cache.get(cache_key); // 캐시에 해당 캐시 키를 가진 데이터 검색
if(cache_data != null){
return_data = cache_data.clone(); // 캐시 내 데이터 변경되지 않도록 복제
return return_data;
}
}
... 쿼리 수행 ....
if(cache != null){ // 캐시를 사용하도록 지정된 sql_id이면
cache_data = return_data.clone(); // 새로이 조회된 데이터 복제하여 캐시에 저장
cache.put(cache_key, cache_data);
}
return return_data;
}
--------------------------------------------------------------
3. Servlet과 같은 Request 호출 시작부분에 Request cache를 초기화하고 서비스 종료후 삭제하는 코드 적용
(아래코드는 이해를 위한 Sample 코드임)
import com.perf;
public class CacheFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterchain) throws IOException, ServletException {
try {
RequestCache.initialize(); // Request Cache 초기화(캐시생성)
filterchain.doFilter(request, response); // 본 업무처리
}finally{
RequestCache.remove(); // 사용한 Request Cache 제거
}
}
}
--------------------------------------------------------------
4. 실제 프로그램 모듈에서 Cache 적용코드 추가
(호출된 서비스 시작부분에 추가하는 것이 중요함)
public class SalesMngt extends Command {
public void proessSale(ServletRequest request, ServletResponse response) {
RequestCache.set("SCUT0001.retrieveCustInfo"); // 특정 SQL ID에 대해 캐시 동작 지정
RequestCache.set("SSAL0002.retrieveProductInfo");
RequestCache.set("SCOM0003.retrieveCommCDInfo");
try {
… 업무 로직 처리 …
}catch(Exception es){
… 생략 …
}
… 생략 …
}
}
'성능개선' 카테고리의 다른 글
채번로직 캐시적용 (0) | 2022.11.18 |
---|---|
Glance sample 스크립트 (0) | 2022.11.18 |
수식계산기 (0) | 2022.11.18 |
날짜연산 개선 (1) | 2022.11.18 |
자배 배치 모니터링용 소스 (0) | 2022.11.18 |