Skip to content

쿼리 성능 개선 ‐ 템플릿 삭제

김경미 edited this page Sep 26, 2024 · 3 revisions

데이터 스펙

멤버: 10 건
카테고리: 100 건 (멤버 당 10 건)
태그: 2000 건 (멤버 당 200 건)
템플릿: 10만 건 (멤버 당 1만 건)
소스 코드: 10만 ~ 50만 건 (템플릿 당 1~5 개 랜덤 생성)

컴퓨터 스펙

윈도우 11
프로세서 AMD Ryzen 9 4900HS with Radeon Graphics 3.00 GH
설치된 RAM 16.0GB
시스템 종류 64비트 운영 체제, x64 기반 프로세서

테스트 조건

10개의 스레드로 100번씩 실행
총 1000번의 요청 실행
최대 테스트 대기 시간은 60초



개선 전

속도 측정

Total request count: 1000
Total elapsed time: 89103ms
Average elapsed time: 89ms

쿼리 분석

총 [8 + 태그 개수 * 2 + 소스 코드 개수] 개 쿼리 실행

1. 템플릿의 Member 조회 (Name 기반)

  • Repository: MemberJpaRepository
  • Method: fetchByName
    select
        m1_0.id,
        m1_0.created_at,
        m1_0.modified_at,
        m1_0.name,
        m1_0.password,
        m1_0.salt 
    from
        member m1_0 
    where
        m1_0.name=?
  • 호출 횟수: 1회

2. 템플릿의 Thumbnail 정보 조회 (Template ID 기반)

  • Method: ThumbnailJpaRepository.deleteByTemplateId 에서 호출
    select
        m1_0.id,
        m1_0.created_at,
        m1_0.modified_at,
        m1_0.name,
        m1_0.password,
        m1_0.salt 
    from
        member m1_0 
    where
        m1_0.name=?
  • 호출 횟수: 1회

3. 템플릿의 Source Code 목록 조회 (ID 기반)

  • Method: ThumbnailJpaRepository.deleteByTemplateId 에서 호출
    select
        sc1_0.id,
        sc1_0.content,
        sc1_0.created_at,
        sc1_0.filename,
        sc1_0.modified_at,
        sc1_0.ordinal,
        sc1_0.template_id 
    from
        source_code sc1_0 
    where
        sc1_0.id=?
  • 호출 횟수: 1회

4. Template 정보 조회

  • Method: ThumbnailJpaRepository.deleteByTemplateId 에서 호출
    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.id=?
  • 호출 횟수: 1회

5. 템플릿의 소스 코드 목록 조회 (Template ID 기반)

  • Repository: SourceCodeJpaRepository
  • Method: deleteByTemplateId
    select
        sc1_0.id,
        sc1_0.content,
        sc1_0.created_at,
        sc1_0.filename,
        sc1_0.modified_at,
        sc1_0.ordinal,
        sc1_0.template_id 
    from
        source_code sc1_0 
    where
        sc1_0.template_id=?
  • 호출 횟수: 1회

6. Template의 Tag 목록 정보 조회 (Template ID 기반)

  • Repository: TemplateTagJpaRepository
  • Method: deleteAllByTemplateId
    select
        tt1_0.tag_id,
        tt1_0.template_id,
        tt1_0.created_at,
        tt1_0.modified_at 
    from
        template_tag tt1_0 
    where
        tt1_0.template_id=?
  • 호출 횟수: 1회

7. Tag 정보 조회 (ID 기반)

  • Repository: TemplateTagJpaRepository
  • Method: deleteAllByTemplateId
    select
        t1_0.id,
        t1_0.created_at,
        t1_0.modified_at,
        t1_0.name 
    from
        tag t1_0 
    where
        t1_0.id=?
  • 호출 횟수: 5회 (태그 개수 만큼)

8. Thumbnail 삭제

  • Repository: ThumbnailJpaRepository
  • Method: deleteByTemplateId
    delete 
    from
        thumbnail 
    where
        id=?
  • 호출 횟수: 1회

9. Source Code 삭제

  • Repository: SourceCodeJpaRepository
  • Method: deleteById
    delete 
    from
        source_code 
    where
        id=?
  • 호출 횟수: 1회 (소스코드 개수 만큼)

10. Template Tag 삭제

  • Repository: TemplateTagJpaRepository
  • Method: deleteAllByTemplateId
    delete 
    from
        template_tag 
    where
        tag_id=? 
        and template_id=?
  • 호출 횟수: 5회 (태그 개수 만큼)

11. Template 삭제

  • Repository: TemplateJpaRepository
  • Method: deleteById
    delete 
    from
        template 
    where
        id=?
  • 호출 횟수: 1회

개선을 위해 필요한 작업

쿼리 최적화

@Modifying을 이용한 삭제 쿼리 개선

템플릿들의 요소를 삭제하는 방법이 현재 deleteByTemplateId 여러번 실행하고 있다.

해당 부분을 한번의 쿼리로 모두 삭제하는 명령어로 변경한다.

@Modifying(clearAutomatically = true)

@Modifying을 사용해 해당 메서드가 데이터베이스의 데이터를 변경하는 쿼리임을 Spring Data JPA에 알릴 수 있다.

  • 영속성 컨텍스트와 데이터 불일치 문제가 생길 수 있다.
    • 영속성 컨텍스트를 비워주는 작업을 도와주는 설정을 추가해야한다.
    • **@Modifying(clearAutomatically=true) 속성을 사용해 변경 후 자동으로 영속성 컨텍스트를 초기화
  • 벌크 연산 시 사용
    • 벌크 연산은 데이터베이스에서 UPDATE, DELETE 시 대량의 데이터를 한 번에 처리하는 작업

참고 자료 : @Modifying 이란?


썸네일 삭제
@Modifying(clearAutomatically = true)
@Query("DELETE FROM Thumbnail t WHERE t.template.id in :templateIds")
void deleteByTemplateIds(List<Long> templateIds);
썸네일 삭제 (Template ID 기반)
  • Repository: ThumbnailJpaRepository
  • Method: deleteByTemplateIds
delete t1_0 
from
    thumbnail t1_0 
where
    t1_0.template_id in (?)

소스코드 삭제
@Modifying(clearAutomatically = true)  
@Query("DELETE FROM SourceCode s WHERE s.template.id in :templateIds")  
void deleteByTemplateIds(List<Long> templateIds);
소스코드 삭제 (Template ID 기반)
  • Repository: SourceCodeJpaRepository
  • Method: deleteByTemplateIds
delete sc1_0 
from
    source_code sc1_0 
where
    sc1_0.template_id in (?)

템플릿태그 삭제
@Modifying(clearAutomatically = true)  
@Query("DELETE FROM TemplateTag t WHERE t.template.id in :templateIds")  
void deleteAllByTemplateIds(List<Long> templateIds);
템플릿태그 삭제 (Template ID 기반)
  • Repository: TemplateTagJpaRepository
  • Method: deleteByTemplateIds
delete tt1_0 
from
	template_tag tt1_0 
where
    tt1_0.template_id in (?)

개선 후

총 7 개 쿼리 실행

속도 측정

Total request count: 1000
Total elapsed time: 51689ms
Average elapsed time: 51ms

쿼리 분석

1. Member 정보 조회 (Name 기반)

  • Repository: MemberJpaRepository
  • Method: fetchByName
    select
        m1_0.id,
        m1_0.created_at,
        m1_0.modified_at,
        m1_0.name,
        m1_0.password,
        m1_0.salt 
    from
        member m1_0 
    where
        m1_0.name=?
  • 호출 횟수: 1회

2. 썸네일 삭제 (Template ID 기반)

  • Repository: ThumbnailJpaRepository
  • Method: deleteByTemplateIds
    delete t1_0 
    from
        thumbnail t1_0 
    where
        t1_0.template_id in (?)
  • 호출 횟수: 1회

3. 소스코드 삭제 (Template ID 기반)

  • Repository: SourceCodeJpaRepository
  • Method: deleteByTemplateIds
    delete sc1_0 
    from
        source_code sc1_0 
    where
        sc1_0.template_id in (?)
  • 호출 횟수: 1회

4. 템플릿태그 삭제 (Template ID 기반)

  • Repository: TemplateTagJpaRepository
  • Method: deleteByTemplateIds
    delete tt1_0 
    from
        template_tag tt1_0 
    where
        tt1_0.template_id in (?)
  • 호출 횟수: 1회

5. Template 정보 조회

  • Repository: TemplateJpaRepository
  • Method: fetchById
    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.id=?
  • 호출 횟수: 1회

6. Template 정보 조회

  • Method: TemplateJpaRepository.deleteById 에서 호출
    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.id=?
  • 호출 횟수: 1회

7. Template 삭제

  • Repository: TemplateJpaRepository
  • Method: deleteById
    delete 
    from
        template 
    where
        id=?
  • 호출 횟수: 1회


성능 개선 결과

개선 전

Total request count: 1000
Total elapsed time: 89103ms
Average elapsed time: 89ms

개선 후

Total request count: 1000
Total elapsed time: 51689ms
Average elapsed time: 51ms

⚡️ 코드zap

프로젝트

규칙 및 정책

공통

백엔드

프론트엔드

매뉴얼

백엔드

기술 문서

백엔드

프론트엔드

회의록


Clone this wiki locally