Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(Problem): 시험, 과목, 자격증 테이블의 역정규화 #85

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

morenow98
Copy link
Member

PR 변경된 내용

역정규화 테이블 ProblemInfo 생성

  • 기존 쿼리는 problem을 조회할 때 exam, subject를 모두 조인하는 성능상 문제 발생
  • 따라서 certificate, exam, subject를 Cartesian Product 한 테이블인 ProblemInfo Entity를 새로 생성
  • 여러 문제가 발생할 수도 있지만, 다음과 같은 이유에서 타당하다고 보았음
    • 기존 certificate, exam, subject 테이블과 정합성의 문제: Problem, Certificate, Exam, Subject, ProblemInfo 모두 서비스 상에서는 조회만 가능한 데이터이고 개발자가 직접 수정하는 데이터이므로 어느 정도 해결 가능하다고 보았음. 또한 외부에서 정합성이 맞는지 체크하는 코드를 가지고 있어, 생성/수정/삭제 시마다 체크할 예정.
    • 데이터 개수가 너무 많을 수 있다는 문제: 현재 존재하는 자격증의 모든 정보를 넣더라도 해당 테이블의 데이터는 최대 100 * 6 * 10 = 6000 개를 넘지 않을 것으로 보임.

Bookmark 여부를 서브쿼리로 가져오던 것에서 join으로 가져오도록 변경

  • 서브쿼리는 효율이 굉장히 좋지 않은데, 기존에 서브 쿼리로 확인하던 문제 발견
  • 따라서 left join으로 변경함. 아래와 같이 64.63% 성능 개선이 됨
    image

랜덤 쿼리 개선

  • 기존에 각 과목마다 최대 20문제를 랜덤으로 가져오는 부분에서 RAND() 함수를 통해 MySQL에 쿼리를 날림

  • 20문제를 모두 가져와서 랜덤으로 섞은 후 원하는 개수를 반환하는 것으로 변경

  • 위 사항들에 따른 테스트 코드 변경

    • Problem을 만들 때 ProblemInfo 와 매핑해야 하는 부분에서 수정이 많이 발생

추가 내용

  • API 명세에 맞게 응답 DTO의 변수명 변경

참조

Closes #76

- Subject, Exam, Certificate의 정보를 합친 ProblemInfo Entity를 만들고, Problem과 매핑
- 기존에 Problem에 서브 쿼리를 날리던 것을 left join으로 바꿔 성능 향상
Copy link
Contributor

@InHyeok-J InHyeok-J left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

역정규화 도입한건 아주 좋은듯 ~
도입 전 후의 성능 차이가 궁금하네
그리고 problemInfo가 DB에 없으면 inner join으로 아무 값이 안나올것 같으니 개발 DB 업데이트가 필수적일듯~

Comment on lines +50 to +51
.join(problem.problemInfo, problemInfo)
.leftJoin(bookmark).on(bookmark.problem.id.eq(problem.id).and(memberIdExistsOrFalse(memberId)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그인 여부랑 상관없이 bookmark를 join하고 있는데 이 부분을 수정이 필요한거 아닌가?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 내가 서브 쿼리로 짰던 이유였어...! 흐음... 수정을 해야겠네.. 감사

Comment on lines +41 to +45
.flatMap(subjectId -> {
List<ProblemWithBookmarkDetailQueryDto> problems = problemRepository.findDetailByExamIdAndSubjectIdWithBookmark(memberId, examId, subjectId, MAX_PROBLEM_COUNT);
Collections.shuffle(problems); // 문제 리스트를 랜덤으로 섞음
return problems.stream().limit(count);
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에는 20문제를 DB에서 랜덤으로 가져왔다고 하면
지금은 20문제만 가져오는 로직인데 여기서 발생하는 차이가 있을 듯?

그 이유는 examId, subjectId에 맞는 값을 가져올 때
examId와 subjectId를 queryDSL에 Nullable 하게 쿼리를 만들어서 전달하는데, subjectId는 현재 로직 상 무조건 받기 때문에 해당 사항은 아니지만 examId는 nullable 하기 때문에

  • examiId가 null이 아닌 경우 -> examId와 subjectId 모두 맵핑 되는 문제는 20문제밖에 존재하지 않아 기존의 로직과 새 로직의 차이가 없음
  • examId가 null인 경우 -> subjectId만 맵핑되는 쿼리 결과가 나오기 때문에 정처기 기준 100+a 에 값들 중 20문제가 랜덤으로 뽑혔으나 지금에는 DB 쿼리 결과가 고정이 될것으로 예상이 됨 -> 클라이언트랑 합의가 된 부분인지??

라고 생각 되는데 혹시 내가 이해한게 맞나? 잘못 이해했다면 말해줘 approve로 바꿀게

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아... 생각을 못했네.. 모든 시험에 대한 걸 전부 다 가져와서 랜덤을 하는 걸로 바꿔야겠지..?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

시험, 과목, 자격증 테이블의 역정규화
2 participants