제가 짠 쿼리 입니다. 같은 카테고리에 자기 자신을 제외한 무작위로 3개의 레슨을 뽑는 경우를 쿼리문을 날려 디비에서 가져왔습니다.
SELECT * FROM lesson l WHERE l.category = ?1 AND l.id <> ?2 ORDER BY RAND() LIMIT 3
이 쿼리는 특정 카테고리(category)에 속하면서 현재 레슨(id)을 제외한 모든 레슨 중에서 무작위로 3개를 선택합니다.
?1 ?2는 파라미터를 의미하며, mysql에서는 RAND() 함수가 있기에 사용했습니다.
데이터베이스에서 무작위 함수를 사용하여 레슨을 선택하면 성능 이슈가 발생할 수 있습니다. 이러한 성능 이슈는 데이터베이스 스코프(scope)와 관련이 있습니다.
무작위 함수를 사용하면 데이터베이스가 모든 레슨 행을 스캔하고 무작위로 정렬해야 합니다. 이 작업은 스코프가 작을 때는 그다지 큰 문제가 되지 않지만, 스코프가 커질수록 성능 문제가 발생할 가능성이 높아집니다.
무작위 선택에 대한 성능 이슈를 고려할 때, 서버에서 이 작업을 수행하는 것이 더 나은 방법일 수 있습니다. 서버에서 레슨 데이터를 검색한 후, 무작위로 3개를 선택하는 방법을 고려할 수 있습니다.
즉, 무작위로 선택하는 로직을 서비스 단에서 짜면 됩니다. 이때 인덱스를 활용하면 됩니다.
서버에서 처리하는 경우, 데이터베이스의 스코프와 관계없이 무작위 선택을 수행할 수 있으며, 레슨 데이터를 미리 캐싱하여 더 빠른 응답 시간을 제공할 수도 있습니다.
저는 다음과 같은 로직을 서비스단에 생성했습니다.
public List<RecommendLesson> getRecommendLessons(LessonCategory category, Long lessonId){
// 레포지토리에 카테고리에 맞는 Lesson 중, 자신을 제외한 Lesson을 모두 가져오는 로직
// 원래 인덱스로 계산하고 한번에 가져오려고 했음........
List<Lesson> filteredLessons = lessonRepository.findByCategoryNotCurrent(category, lessonId);
// 무작위로 선택할 레슨의 개수
// 위에 필터된 레슨의 크기가 작으면 그걸로 설정됨
int numRandomLessons = Math.min(3, filteredLessons.size());
List<Lesson> randomLessons = new ArrayList<>();
Random random = new Random();
while (randomLessons.size() < numRandomLessons) {
int randomIndex = random.nextInt(filteredLessons.size());
Lesson randomLesson = filteredLessons.get(randomIndex);
// 중복된 레슨을 선택하지 않도록 필터링
if (!randomLessons.contains(randomLesson)) {
randomLessons.add(randomLesson);
}
}
return randomLessons.stream()
.map(RecommendLesson::new)
.toList();
}
무작위로 데이터를 선택하는 경우, 데이터베이스에서 처리할 때 성능 이슈를 고려해야 합니다. 스코프가 큰 경우, 서버에서 무작위 선택을 수행하는 것이 더 효율적일 수 있습니다. 데이터베이스와 서버 간의 작업 분배를 고려하여 최적의 성능을 얻을 수 있도록 설계하는 것이 중요합니다.