728x90
오늘은 다중조인이 들어가지않은 단순 paging 처리가 되어있는코드에 성능테스트를 해보겠습니다.
ngrinder 의 구성부터 보여드리겠습니다!
1개의 초당 50번의 요청을 하였고 약 1분간 진행하였습니다.
@Override
public Page<Board> getBoardList(BoardSearchCondition condition, Pageable pageable) {
String[] word = (condition.getActivityField()!=null) ? condition.getActivityField().split("/") : null;
BooleanExpression activityFieldExpression = (word != null) ? activityFieldContains(word) : null;
JPAQuery<Board> contentQuery = selectFrom(board)
.where(categoryEq(condition.getCategory()),recruitmentPeriodPredicate(),activityFieldExpression)
.orderBy(getOrderSpecifier(pageable.getSort()).stream().toArray(OrderSpecifier[]::new))
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
JPAQuery<Long> countQuery = select(board.count())
.from(board)
.where(categoryEq(condition.getCategory()),recruitmentPeriodPredicate(),activityFieldExpression);
return PageableExecutionUtils.getPage(contentQuery.fetch(),pageable,()->countQuery.fetchCount());
}
/**
* 진행 , 마감 중 어떤 값이 들어오든 동적으로 검색해주는
*/
private BooleanExpression boardStateEq(List<BoardStateEnum> boardStates) {
if (boardStates == null || boardStates.isEmpty()) {
return null;
}
return boardStates.stream()
.map(state -> {
if (state == BoardStateEnum.ONGOING) {
return recruitmentPeriodPredicate();
}
if (state == BoardStateEnum.CLOSED) {
return closedRecruitmentPredicate();
}
return null;
})
.filter(Objects::nonNull)
.reduce(BooleanExpression::or)
.orElse(null);
}
/**
* 진행 중 활동
*/
private BooleanExpression recruitmentPeriodPredicate() {
StringExpression recruitmentPeriodEndDate = Expressions.stringTemplate("STR_TO_DATE(SUBSTRING_INDEX({0}, '~', -1), '%Y.%m.%d')", board.recruitmentPeriod);
return recruitmentPeriodEndDate.goe(String.valueOf(LocalDate.now()));
}
/**
* 마감 된 활동
*/
private BooleanExpression closedRecruitmentPredicate() {
StringExpression recruitmentPeriodEndDate = Expressions.stringTemplate("STR_TO_DATE(SUBSTRING_INDEX({0}, '~', -1), '%Y.%m.%d')", board.recruitmentPeriod);
return recruitmentPeriodEndDate.lt(String.valueOf(LocalDate.now()));
}
/**
* 동적 orderby
*/
private List<OrderSpecifier> getOrderSpecifier(Sort sort){
List<OrderSpecifier> orders=new ArrayList<>();
sort.stream().forEach(order->{
Order direction = order.isAscending() ? Order.ASC : Order.DESC;
String property = order.getProperty();
PathBuilder orderByExpression = new PathBuilder(Board.class, "board");
orders.add(new OrderSpecifier(direction,orderByExpression.get(property)));
});
return orders;
}
/**
* 카테고리 동적 검색
*/
private BooleanExpression categoryEq(String category) {
return hasText(category) ? board.category.eq(category) : null;
}
/**
* 활동영역 동적처리
*/
private BooleanExpression activityFieldContains(String[] activityFields) {
BooleanExpression result = null;
for (String field : activityFields) {
if (hasText(field)) {
BooleanExpression fieldExpression = board.activityField.like("%" + field + "%");
// or 을 처리하여 split 한 word 가 포함되어 있는지 확인해서 넣어줌
// 예 문학/시나리오 (board.activityField.like("%문학%").or(board.activityField.like("%시나리오%")))
result = (result == null) ? fieldExpression : result.or(fieldExpression);
}
}
return result;
}
/**
* boardId 동적검색
*/
private BooleanExpression boardIdEq(Long boardId) {
return hasText(String.valueOf(boardId)) ? board.id.eq(boardId) : null;
}
/**
* 검색어 단어를 통한 동적검색
*/
private BooleanExpression searchWordExpression(String searchWord) {
return Optional.ofNullable(searchWord) //seachWord가 null이 아닌경우에 Optional로 감싸기
.filter(word->!word.isEmpty()) // searchWord가 비어 있지 않은경우에만 map 함수
.map(word-> Stream.of(board.activityName.containsIgnoreCase(word),
board.activityDetail.containsIgnoreCase(word),
board.activityField.containsIgnoreCase(word))
.reduce(BooleanExpression::or) // 위 조건들을 OR 연산으로 묶음
.orElse(null))
.orElse(null);// // 만약 조건이 없으면 null 반환
}
이처럼 동적쿼리 및 페이징쿼리가 있는 board 를 entity조회방식으로 불러왔을 때 성능테스트 실시하였습니다.
t2.micro 기준으로해서 TPS 약 27.4가 나왔습니다 아무리 프리티어래도 너무나도 낮은수치같았습니다. 그래서 dto조회방식으로 변경해보기로 했습니다. dto 에 querydsl의 queryproject어노테이션을 붙이고 dto조회방식으로 조회했습니다.
@Override
public Page<BoardResponseDto.BoardSimpleListResponseDto> getBoardList(BoardSearchCondition condition, Pageable pageable) {
String[] word = (condition.getActivityField()!=null) ? condition.getActivityField().split("/") : null;
BooleanExpression activityFieldExpression = (word != null) ? activityFieldContains(word) : null;
JPAQuery<BoardResponseDto.BoardSimpleListResponseDto> contentQuery = new JPAQueryFactory(getEntityManager())
.select(new QBoardResponseDto_BoardSimpleListResponseDto(
board.id,
board.activityName,
board.activityImg,
board.category,
board.view,
board.likeCount,
board.recruitmentPeriod,
Expressions.numberTemplate(
Integer.class,
"DATEDIFF(STR_TO_DATE(SUBSTRING_INDEX({0}, ' ~ ', -1), '%y.%m.%d'), CURRENT_DATE)",
board.recruitmentPeriod
),
board.comments.size()
))
.from(board)
.where(categoryEq(condition.getCategory()), recruitmentPeriodPredicate(), activityFieldExpression)
.orderBy(getOrderSpecifier(pageable.getSort()).stream().toArray(OrderSpecifier[]::new))
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
JPAQuery<Long> countQuery = select(board.count())
.from(board)
.where(categoryEq(condition.getCategory()),recruitmentPeriodPredicate(),activityFieldExpression);
return PageableExecutionUtils.getPage(contentQuery.fetch(),pageable,()->countQuery.fetchCount());
}
약 3배정도의 성능차이를 보이네요 기존 27 -> 78.2 tps 로 확실하게 차이가 분명했습니다.
궁금하고 앞으로 테스트해봐야 할 것
t2.micro -> t2.small 로 늘렸을 때 cpu를 2배로 업그레이드하게되면 tps도 정확하게 2배가 늘어나게 될까 ?
where 문에 있는 컬럼을 index조회방식으로 조회하게 되면 성능은 얼마나 좋아질 것 인가 ? where 문에 있다고 무조건 다 index를 타게 될까 ?
728x90
'성능테스트' 카테고리의 다른 글
mysql like함수 조회성능개선 (0) | 2024.02.09 |
---|