Closed Gunyoung-Kim closed 3 years ago
위 메소드의 성능을 개선하기 위해 in절을 사용하기로 했다.
아래는 in절을 활용한 코드다.
public interface MuscleRepository extends JpaRepository<Muscle,Long> {
@Query("SELECT m FROM Muscle m "
+ "WHERE m.name in :muscleNames")
public List<Muscle> findAllByNameInQuery(@Param("muscleNames") List<String> muscleNames);
}
public class MuscleServiceImpl implements MuscleService {
@Override
@Transactional(readOnly = true)
public List<Muscle> test(List<String> muscleNames) {
return muscleRepository.findAllByNameInQuery(muscleNames);
}
}
기존의 방식은 table full scan을 muscle name의 개수만큼 실행되었다면 in절을 활용한 방식은 한번의 table full scan을 통해 원하는 바를 이룰 수 있어 성능 개선을 기대할 수 있다.
새로운 쿼리와 기존의 쿼리를 이용하여 테스트를 진행해보았다.
테스트 환경은 총 10만 2천개의 muscle row를 추가했다.
아래는 해당 테스트 코드이다.
@Test
public void test() {
List<String> muscleNames = getMuscleNames();
long startTime = System.nanoTime();
// List<Muscle> result = muscleService.getMuscleListFromMuscleNameList(muscleNames);
List<Muscle> result = muscleService.test(muscleNames);
long endTime = System.nanoTime();
System.out.println("Execution time: " + (endTime - startTime) +"\nNum of result: " + result.size());
}
private List<String> getMuscleNames() {
List<String> muscleNames = new ArrayList<>();
int totalMuscleNum = 100000;
int resultNum = 1000;
for(int i=0;i<totalMuscleNum;i++) {
if(i % (totalMuscleNum/resultNum) == 1) {
muscleNames.add("gun" + i);
}
}
return muscleNames;
}
getMuscleNames에서 resultNum은 메소드 실행했을때 몇개의 결과를 가져올 지 결정하는 변수이다. resultNum이 10개, 100개, 1000개인 경우에 대해서 테스트를 진행하였다. 실제 배포 환경에서 이 메소드는 ExerciseMuscle을 추가할때 사용되기 때문에 in 절안에 테스트 환경처럼 100개, 1000개 까지는 들어가지 않을 것이다.
그리고 이에 대한 테스트 결과이다.
새로운 방식의 메소드가 기존의 것보다 같은 갯수에서 월등히 빠른것을 알 수 있다. 그리고 기존의 방식은 결과의 갯수가 n배 늘어나면 실행시간도 n배 늘어나는 것을 볼 수 있다.
쿼리 변경에 따라 메소드에도 약간의 변화가 필요해졌다. 왜냐하면 기존의 방식은 name들을 돌면서 해당 name의 Muscle이 존재하지 않으면 MuscleNotFoundedException을 던지도록 했지만 이제는 일일히 돌지 않고 name들을 한데모아 한 쿼리로 처리하기에 이러한 예외 처리방식은 유효하지 않게 되었다. 이제는 해당 name의 Muscle이 존재하지 않는 경우에 대해서는 예외를 던지지 않고 쿼리의 결과에서 해당 name에 대한 Muscle을 추가하지 않는 방식으로 변경했다. 예를 들어 "kim", "gun" 이름들에 해당하는 Muscle들을 찾도록 서비스 클래스에 요청이 들어오고 "kim"이라는 이름을 가진 Muscle만 존재한다면 서비스클래스가 내놓은 List 안에는 "kim"이라는 이름을 가진 Muscle만 들어가 있을 것이다.
반영 commit : https://github.com/Gunyoung-Kim/Touch-My-Body/commit/ccf673341d99e7d973dbbb418cb70e3a59a5ffc0
아래 코드의 getMuscleListFromMuscleNameList 가 성능 개선을 하려는 target 메소드다.
-> 위 코드를 보면 getMuscleListFromMuscleNameList 내부에서 입력된 muscle name의 개수만큼의 select 쿼리를 발생 시키는 것을 알 수 있다. 예를 들어 입력된 Muscle name의 개수가 20개라면 20개의 select 쿼리가 발생하는것이다. 이는 굉장히 비효율적이라 볼 수 있다. 그래서 이에 대해 개선을 해보고자 한다.