약 500만건의 데이터에 대해서 페이징 처리 된 페이지를 조회해오는데 너무 오랜 시간이 걸리는 문제가 발생했다.
전체 상품 조회에 대한 페이징 처리라 index도 많은 도움이 되지 않았다.
@Override
public Page<AllProductResponseDto> findAllDto(Pageable pageable) {
List<AllProductResponseDto> content = queryFactory
.select(Projections.constructor(AllProductResponseDto.class,
product.id,
product.productName,
product.price,
product.mallName,
product.category
)).from(product)
.orderby(product.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPAQuery<Long> countQuery = queryFactory
.select(product.count())
.from(product);
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
그래서 발견한 문제점이 select 쿼리는 크게 시간이 소요되지는 않지만
count query가 대부분의 시간을 잡아먹고있었다.
한페이지 조회하는데 약 24초의 시간이 걸렸다.
총 count의 수를 미리 cache에 저장해두고 페이지 조회시에 캐시에서 가져오는 방식을 생각했다. 해당 방식을 저장 할 수 있었던 이유는
때문에 매일 12시에 스케줄러를 통해 총 상품 개수를 저장해두도록 하고 count 쿼리를 page 조회 매서드에서 삭제했다.
@Override
public Page<AllProductResponseDto> findAllDto(Pageable pageable,Long count) {
List<AllProductResponseDto> content = queryFactory
.select(Projections.constructor(AllProductResponseDto.class,
product.id,
product.productName,
product.price,
product.mallName,
product.category
)).from(product)
.orderBy(product.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
return new PageImpl<>(content, pageable, count);
}
public Page<AllProductResponseDto> getAllProducts(Pageable pageable) {
int pageNumber = pageable.getPageNumber();
// 인덱싱 적용 고려중
Long count = allProductRedisRepository.getProductCount().orElseGet(() -> {
long countResult = productRepository.count();
allProductRedisRepository.saveProductCount(countResult);
return countResult;
});
Page<AllProductResponseDto> productsInDB = productRepository.findAllDto(pageable,count);
return productsInDB;
}
###ㄴ 테스트 환경
인원 : 100명
시간 : 5초
루프 : 10번
100명 기준으로 거의 모든 요청이 Timeout이 발생해 에러가 발생했던 이전과 다르게 에러율을 0%로 낮췄고 처리량 또한 대폭 증가했다.
아직 Troughput이 여유가 있는 것으로 보여 인원을 최대한 증가시켜보았다.
인원 : 1500명
시간 : 5초
루프 : 10번
처리량은 602.7/sec 까지 증가했고
인원을 1500명 이상으로 세팅 시, EC2 CPU의 병목 현상으로 처리량이 반으로 줄어드는 결과가 나왔다.
단순히 page 처리를 했지만 워낙 데이터가 많다보니 조회 쿼리에서 많은 시간이 소요 되는지 알았는데 실제로 문제가 된것은
page 처리를 위해 총 데이터 개수를 구하는 count 쿼리가 문제가 되고 있었다.
이를 미리 REDIS(Elastic Global Cache)에 저장하고 조회해 오면서
100명에 대해서도 거으 100% 의 에러율을 보이던 이전과 다르게
1500명의 인원에 대해서 초당 600 이상의 처리량을 보이도록 성능을 향상 시킬 수 있었다.