금쪽 같은 트러블 슈팅 (최적화)
1. DB 조회 쿼리 최적화
1-1. Projection 을 통해 필요한 값만 가져오기
- 전체 필드가 아닌 필요한 필드만 받아오는 방법
- 적용 전 : SELCT * FROM User;
- 적용 후 : SELECT username, profileImageUrl FROM User;
List<UserProfile> findById(Long userId);
public interface UserProfile {
String getUsername();
String getProfileImageUrl();
}
1-2. 하이버네이트 @BatchSize
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@Embedded
private Address address;
@org.hibernate.annotations.BatchSize(size = 5)
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<Order>();
...
1-3. 읽기 전용 쿼리 힌트 적용
하이버네이트 전용 힌트인 readOnly를 적용한다. (영속성 컨텍스트 관리에서 제외됨)
TypedQuery<Order> query = em.createQuery("select o from Order o", Order.class);
query.setHint("org.hibernate.readOnly", true);
1-4. 읽기 전용 트랜젝션 사용
스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있다.
@Transactional(readOnly = true)
트랜잭션을 읽기모드로 적용하면 강제로 플러시를 호출하지 않는 이상 플러시가 발생하지 않는다.
그러므로 자동으로 플러시 되면서 수행되는 무거운 로직들이 실행되지 않아 성능이 향상된다.
1-5. 트랜잭션 밖에서 읽기
말 그대로 트랜잭션 밖에서 엔티티를 조회하는 것임으로 커밋, 플러시 어떤 것도 적용되지 않는다.
그러므로 반드시 조회목적인 경우에만 사용해야 됨.
@Trnasactional(readOnly = true)
public proccessA() {
Entity entity = search();
entity.getA(); // A 가 LazyLoading 연관 객체일 경우
}
public proccessB() {
Entity entity = search();
entity.getB(); // B 가 LazyLoading 연관 객체가 아닐 경우 (EAGER , Column 필드)
}
@Trnasactional(readOnly = true)
public search() {
//.. 조회하는 쿼리
return entity;
}
결과적으로 읽기 전용 데이터를 조회할때 메모리를 최적화하려면 1) Projection 조회, 2) 하이버네이트가 제공하는 읽기 전용 쿼리 힌트
@Transactional(readOnly = true)
@Override
public Page<Thread> search(ThreadSearchCond cond, Pageable pageable) {
var query = query(thread, cond)
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
query.orderBy(thread.mentions.any().createdAt.desc());
query.setHint(AvailableHints.HINT_READ_ONLY, true);
var threads = query.fetch();
long totalSize = countQuery(cond).fetch().get(0);
threads.stream()
.map(Thread::getComments)
.forEach(comments -> comments
.forEach(comment -> Hibernate.initialize(comment.getEmotions())));
return PageableExecutionUtils.getPage(threads, pageable, () -> totalSize);
}
출처 : https://jinjinyang.tistory.com/42
JPA 성능 최적화
N+1 문제 JPA 성능상 가장 주의해야 되는 문제이다. 먼저, N +1 문제는 지연로딩, 즉시로딩 모든 경우에 발생할 수 있다. 1) 즉시로딩 N+1문제 즉시로딩의 경우 엔티티 매니저를 통해 조회할 경우 즉
jinjinyang.tistory.com
6. 하이버네이트 @Fetch(FetchMode.SUBSELECT) 주의하기!
FetchMode.SUBSELECT 을 사용할 경우 연관된 데이터를 서브 쿼리를 통해 N + 1문제를 해결할 수 있다.
하지만 부모 쿼리 row 개수만큼 실행되기 때문에 성능이 최악이다. (즉, 부모 쿼리 ROW가 몇개 안될때만 사용해야한다.)
select *
from orders
where member_id in (
select id
from member
); // orders Row 갯수만큼 수행됨
하지만! 서브셀렉트는 매우 느립니다.
@Fetch(FetchMode.SUBSELECT) 과 IN subquery 는 왜 느릴까?
들어가기 JPA 를 활용하다가 문제를 하나 맞딱뜨렸습니다. 퇴근하기 10분전, 동료분의 호출로 위 그림처럼 시간이 오래걸리는 경우가 발생했습니다. 처음 보는 코드에서 어디서 발생했는지 알 수
happy-coding-day.tistory.com