-
Notifications
You must be signed in to change notification settings - Fork 8
Query Performance Improvement Tag Retrieval
- Members: 10 records
- Categories: 100 records (10 per member)
- Tags: 2000 records (200 per member)
- Templates: 100,000 records (10,000 per member)
- Source Codes: 100,000 to 500,000 records (1 to 5 randomly generated per template)
- Executed 100 times with 10 threads
- Total of 1000 requests executed
- Tags used: 20 tags (all existing tags)
- Source Codes: 2 codes
- Total request count: 1000
- Total elapsed time: 5,268,308 ms
- Average elapsed time: 5,268 ms
Total [2 + number of tags] queries executed
-
Repository:
TemplateJpaRepository
-
Method:
findByMemberId
SELECT
t1_0.id,
t1_0.category_id,
t1_0.created_at,
t1_0.description,
(SELECT COUNT(*)
FROM likes
WHERE likes.template_id = t1_0.id),
t1_0.member_id,
t1_0.modified_at,
t1_0.title
FROM
template t1_0
WHERE
t1_0.member_id = ?
- Number of Calls: 1 time
-
Repository:
TemplateTagJpaRepository
-
Method:
findDistinctByTemplateIn
SELECT
DISTINCT tt1_0.tag_id
FROM
template_tag tt1_0
WHERE
tt1_0.template_id IN (?, ?, ?, ?) # as many as the number of templates
- Number of Calls: 1 time
-
Repository:
TagJpaRepository
-
Method:
fetchById
SELECT
t1_0.id,
t1_0.created_at,
t1_0.modified_at,
t1_0.name
FROM
tag t1_0
WHERE
t1_0.id = ?
- Number of Calls: 200 times (as many as the number of tags)
Covering Index
- An index that contains all the data required to satisfy the query.
- If all columns used in SELECT, WHERE, ORDER BY, GROUP BY, etc., are components of the index.
There exists a logic that retrieves all templates for a given member ID.
However, in reality, only the template ID is utilized after this logic.
Thus, we will modify the logic to retrieve only the template IDs.
This change will allow us to utilize the covering index, improving query performance.
@Query("""
SELECT t.id
FROM Template t
WHERE t.member.id = :memberId
""")
List<Long> findAllIdsByMemberId(Long memberId);
When dealing with vast datasets, the IN clause can lead to performance degradation. In our code, the logic for retrieving template tags contains datasets in the IN clause (currently 100,000).
We will improve this by using subqueries.
Using a subquery can enhance the performance of the IN clause. A subquery is a query that is included within the main query and is useful for dynamically retrieving data.
By dynamically filtering data with a subquery, we can efficiently query data from indexed columns.
By using a subquery, we will combine the logic for retrieving template IDs based on member ID and retrieving template tags, thereby improving the performance of the IN clause.
Reference: SQL IN Clause Tuning
Previously, after retrieving tags related to a template from the template tags, we queried the tag table one by one. This caused the tag retrieval logic to execute as many times as there were tags.
To solve this problem, we will merge the logic for retrieving template tags and the logic for retrieving tags.
@Query("""
SELECT DISTINCT t
FROM Tag t
WHERE t.id IN (
SELECT DISTINCT tt.id.tagId
FROM TemplateTag tt
WHERE tt.id.templateId IN
(SELECT te.id FROM Template te WHERE te.member.id = :memberId)
)
""")
List<Tag> findDistinctTagNameByMemberIdIn(Long memberId);
- Total request count: 1000
- Total elapsed time: 3,632,279 ms
- Average elapsed time: 3,632 ms
- Total request count: 1000
- Total elapsed time: 2,704,116 ms
- Average elapsed time: 2,704 ms
- Total request count: 1000
- Total elapsed time: 92,743 ms
- Average elapsed time: 92 ms
Total 1 query executed
-
Repository:
TemplateTagJpaRepository
-
Method:
findDistinctTagNameByMemberIdIn
SELECT
DISTINCT t1_0.id,
t1_0.created_at,
t1_0.modified_at,
t1_0.name
FROM
tag t1_0
WHERE
t1_0.id IN (SELECT
DISTINCT tt1_0.tag_id
FROM
template_tag tt1_0
WHERE
tt1_0.template_id IN (SELECT
t2_0.id
FROM
template t2_0
WHERE
t2_0.member_id = ?))
- Number of Calls: 1 time
- Total request count: 1000
- Total elapsed time: 5,268,308 ms
- Average elapsed time: 5,268 ms
- Total request count: 1000
- Total elapsed time: 92,743 ms
- Average elapsed time: 92 ms
- 백엔드 코드 컨벤션
- 백엔드 기술 스택 및 선정 이유
- 각종 인스턴스 설정 파일 및 구성 위치 가이드
- ERD
- 백엔드 CI CD 동작 프로세스
- 로컬 DB 환경 설정
- 백엔드 로깅 전략
- 백엔드 로그 모니터링 구성도
- 스프링 메트릭 모니터링 구성도
- Flyway 로 스키마 관리
- 코드잽 서버 구성도
- Git Submodule 사용 메뉴얼
- 프론트엔드 코드 컨벤션
- 프론트엔드 기술 스택 및 선정 이유
- 프론트엔드 서비스 타겟 환경 및 브라우저 지원 범위 선정
- 프론트엔드 모니터링 및 디버깅 환경 구축
- 프론트엔드 테스트 목록
- 프론트엔드 라이브러리 기술 검토
- 프론트엔드 개발서버, 운영서버 빌드 및 배포 환경 구분
- 목표했던 타겟 환경과 디바이스에서 서비스 핵심 기능 동작 확인
- 프론트엔드 접근성 개선 보고서
- EC2 로그 확인 방법
- VSCode를 통한 EC2 인스턴스 SSH 연결 방법
- 터미널을 통한 EC2 인스턴스 SSH 연결 방법
- NGINX 설정 파일 접근 및 적용 방법
- DB 접속 및 백업 방법
- [QA] 배포 전 체크리스트
- CI 파이프라인 구축
- CD 파이프라인 구축
- 백엔드 CI CD 트러블슈팅
- Lombok Annotation Processor 의존성을 추가한 이유
- 2차 스프린트 기준 ERD
- DTO 검증하기
- ProblemDetail
- Fork된 레포지토리 PR에서 CI Secrets 접근 문제 해결
- AWS CloudWatch 모니터링
- 스프링 메트릭 모니터링 구축 방법
- 로깅과 Logback에 대해 알아보아요.
- Logback MDC로 쉽게 요청 추적하기 (+ Grafana로 추적 더더 쉽게!)
- 백엔드 CD 파이프라인 Ver.2
- 요청, 응답 로그에 correlationId 를 추가하자!
- 3차 스프린트 기준 ERD
- 더미데이터 생성하고 실행하기
- 쿼리 성능 개선 결과
- 테이블별 인덱스 설정 목록
- 사용자 증가 시 발생할 수 있는 문제 상황과 개선 방안
- k6를 사용한 서버 부하 테스트
- 6차 스프린트 기준 ERD
- TestExecutionListenr 간의 충돌 문제에 대해 알아보아요
- Query Performance Improvement Results
- 테스트 전략 및 CI 설정
- CI CD 구조
- 배포 전, 로컬에서 로그인 기능 포함 테스트해보는 법
- stylelint 적용기
- 내 작업 브랜치 중간에 Merge된 동료의 작업물을 넣고 싶다면 pull vs rebase
- [TS] Webpack config
- [TS] Webpack 환경에서 MSW v2 이슈
- [TS] webpack에서 react‐router‐dom 적용 안됨