[Querydsl] 성능개선 - 3편 ( group by, 커버링 인덱스, update )

2021. 2. 4. 07:00·Develop/spring-data
반응형

이 글은 우아한 형제들 콘서트에서 이동욱님의 영상을 보고 정리를 위한 글입니다.

이 글에 작성된 예시는 모두 Github에 올려두었습니다.

나를 닮았다고 한다...

 

Group By 최적화

Mysql 사용 시 index가 걸려있지 않는 컬럼을 group by 할 경우 file sort가 발생합니다.

 

Index가 걸려있지 않는 경우

select * 
  from house
 group by name

file sort가 발생

 

Index가 걸려있는 경우

select * 
  from house
 group by id

file sort 미발생

 

file sort가 발생하면 성능이 떨어질 수 밖에 없습니다. 이 경우 order by 절에 null을 넣으면 file sort가 발생하지 않습니다.

select * 
  from house 
 group by name
 order by null

file sort 미발생

 

하지만 안타깝게도 Querydsl-JPA 에서는 order by 에 null을 할당할 수 없습니다. 

 

쿼리에 order by 절에 null을 세팅하는 것과 동일하게 Querydsl을 사용하려면 OrderSpecifier 를 상속받아 사용하면 됩니다. 그리고 order by 절에 사용하면 됩니다.

public class OrderByNull extends OrderSpecifier {
    public static final OrderByNull DEFAULT = new OrderByNull();

    public OrderByNull(Order order, Expression target, NullHandling nullhandling) {
        super(order, target, nullhandling);
    }

    public OrderByNull(Order order, Expression target) {
        super(order, target);
    }

    private OrderByNull() {
        super(Order.ASC, NullExpression.DEFAULT, NullHandling.Default);
    }
}
public List<House> findByHouseGroupByNameNotFileSort() {
    return queryFactory
            .selectFrom(house)
            .groupBy(house.name)
            .orderBy(OrderByNull.DEFAULT)  // use
            .fetch();
}

 

커버링 인덱스

커버링 인덱스는 쿼리의 모든 절에서 사용하는 컬럼이 인덱스에 포함된 상태를 말합니다. B-Tree 인덱스를 스캔하는 것만으로도 원하는 데이터를 조회할 수 있습니다. 이 말은 곧 데이터 블록을 읽지 않아도 되기 때문에 쿼리의 성능을 향상 시킬 수 있습니다. 

B-Tree란?
Index의 여러가지 구조중 가장 보편적으로 사용되는 기법입니다.
  - Binary Search Tree에서 개선 된 기법 
  - 인덱스 레코드로 컬럼이 위치한 주소값을 찾아 빠르게 내는 기법
// 인덱스 커버링 적용
// id는 Private key 이다.
// name은 index 이다.
select id
  from house
 where name = ''
 group by id

 

위 쿼리를 explain하면 Using index for group-by를 확인할 수 있습니다. 즉 인덱스 커버링이 적용되었다는 것을 알 수 있습니다.

 

그럼 보편적으로 인덱스 커버링 쿼리 사용하는 사례를 살펴보고 Querydsl-JPA에 적용하는 방법을 알아보겠습니다.

select s.*
  from staff s
  join (
         select id		// 인덱스 커버링 적용
         from house
         where name = '강남집'
  ) h on s.house_id = h.id
  

 

안타깝게도 위 쿼리와 똑같은 Querydsl은 작성할 수 없습니다. 이유는 JPQL은 from절에 서브쿼리를 작성할 수 없기 때문입니다. 그리하여 커버링 인덱스 조회를 나눠서 작성합니다.

public List<Staff> findByCoveringIndex(String name) {
    List<Long> houseIds = queryFactory  // 커버링 인덱스 적용
            .select(house.id)
            .from(house)
            .where(house.name.eq(name))
            .fetch();

    if (houseIds.isEmpty()) {
        return new ArrayList<>();
    }

    return queryFactory
            .selectFrom(staff)
            .join(staff.house, house)
            .where(house.id.in(houseIds))  // 조회된 id 리스트 적용
            .fetch();
}

 

Update

무분별한 DirtyChecking은 피하는 것이 좋습니다. 

 

예시로 아래와 같은 코드를 실행하면 update를 House 객체만큼 실행할 것 입니다. 

// bad example
@Transactional
public void badUpdate() {
    List<House> houses = houseRepository.findAll();

    for (House house : houses) {
        house.changeAddress("강남구 완전 중심");
    }
}

 

일괄적으로 업데이트 할 경우에는 1번의 update문으로 처리하는 것이 성능적으로 좋습니다.

@Transactional
public Long updateAddressByName(String name) {
    return queryFactory
            .update(house)
            .set(house.address, "대박 중심")
            .where(house.name.eq(name))
            .execute();
}

 

결론

DirtyChecking

  - 실시간 비즈니스 처리

  - 실시간 단건 처리

Querydsl.update

  - 대량의 데이터를 일괄로 update 처리

반응형

'Develop > spring-data' 카테고리의 다른 글

[Querydsl-JPA] 자주 사용하는 기능 정리 (Kotlin)  (3) 2021.05.10
Querydsl Join Table Sort 적용 ( 번외로 Pageable와 비슷한 것을 구현해보자! )  (0) 2021.03.14
[Querydsl] 성능개선 - 2편 ( N + 1 )  (0) 2021.02.01
[Querydsl] 성능 개선 1편  (0) 2021.01.29
[Querydsl-JPA] Querydsl JPA를 사용하며.. "*" 아스타리스크 사용방법  (0) 2020.12.20
'Develop/spring-data' 카테고리의 다른 글
  • [Querydsl-JPA] 자주 사용하는 기능 정리 (Kotlin)
  • Querydsl Join Table Sort 적용 ( 번외로 Pageable와 비슷한 것을 구현해보자! )
  • [Querydsl] 성능개선 - 2편 ( N + 1 )
  • [Querydsl] 성능 개선 1편
에디개발자
에디개발자
------ 한발자국씩 성장하자 ------ Github: https://github.com/yongtaelim LinkedIn: https://www.linkedin.com/in/%EC%9A%A9%ED%83%9C-%EC%9E%84-622b69218/
    250x250
  • 에디개발자
    에디블로그
    에디개발자
    • 분류 전체보기 (207) N
      • Develop (51)
        • spring-data (28)
        • spring-batch (7)
        • devops (5)
        • java (5)
        • kotlin (3)
        • database (2)
      • MindControl (12)
      • TroubleShooting (16)
      • Study (76)
        • kotlin (16)
        • java (15)
        • spring (6)
        • test (4)
        • message-queue (10)
        • object (22)
      • Develop Tool (1)
      • Daily Develop (7)
      • Book (5)
      • AI (27) N
        • Claude (12) N
        • ChatGPT (6) N
        • Cursor (3) N
        • Gemini (4) N
        • 트랜드 (2)
      • 개발 트렌드 (8) N
        • 데일리 픽 (8) N
  • 인기 글

  • 태그

    queryDSL
    JPQL
    에디
    Ai
    코틀린
    개발자 도구
    java
    프로그래밍
    엘레강트
    OpenAI
    클로드
    claude
    kotlin
    ChatGPT
    ai 정보
    anthropic
    AI 활용
    객체지향
    스터디
    JPA
  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에디개발자
[Querydsl] 성능개선 - 3편 ( group by, 커버링 인덱스, update )
상단으로

티스토리툴바