[WARN] firstResult/maxResults specified with collection fetch; applying in memory!

charco·2021년 9월 6일
0

!ERROR!

목록 보기
15/17
post-thumbnail

프로젝트 진행중 이런 경고가 뜬다.
메모리를 많이 사용한다는 것인가?
아무튼 경고이기 때문에 좋지는 않은 상황이다.

나는 알 수 없는 문제가 생겼을때 한국 블로그를 제외하고는 StackOverFlow 나 Baeldung을 가장 많이 활용한다.
오늘은 StackOverFlow에서 원하던 정보를 찾았다.


문제 정의

firstResult/maxResults specified with collection fetch; applying in memory!
라는 경고가 떴다.
동작에 문제는 없지만 메모리 낭비를 한다는 내용인 것 같다.
경고를 보아 아마 fetch join 시 offset limit 을 걸어둔 것과 연관이 있는 것 같다.

찾아보니 위 경고가 의미하는 것은
fetch join 과 pagination 을 같이 할 시
"모든 데이터"를 전부 가져와 메모리에서 걸러낸다는 것이다.
이것은 당장 해결해야한다.


원인

일단 문제되는 코드를 보자.

    @Override
    public List<Person> findAll(Page page, OrderSpecifier sort, BooleanExpression keyword) {
        return joinQuery()
                .where(keyword)
                .orderBy(sort)
                .offset(page.getOffset())
                .limit(page.getAmount()).fetch();

    }

    private JPAQuery<Person> joinQuery(){
    	QA a = QA.a;
        return queryFactory.selectFrom(qA)
                .leftJoin(a.b, QB.b)
                .fetchJoin()
                .leftJoin(a.c, QC.c)
                .fetchJoin()
                .leftJoin(a.d, QD.d)
                .fetchJoin();
    }

A B C D 중에 A 와 C 는 1 : N 관계이다.
하나의 A 가 여러개의 C를 가질 수 있기 때문에
A 가 중복되어 반환될 수 있다.

A1 C1
A1 C2
A1 C3

이 상태에서는 페이지네이션으로 원하는 만큼의 A를 가져올 수 없다.
그래서 Hibernate 는 데이터를 모두 가져온 후
JVM 메모리상에서 필터링을 수행한다.

콘솔에 찍힌 쿼리를 봣더니
실제로 페이지네이션에 대한 부분이 빠져있었다.

위에서 말한대로 모든 데이터를 가져와서 JVM 메모리에서
페이지네이션을 한다는 것이다.
지금은 더미데이터밖에 없어서 괜찮을지 몰라도
만에하나 내 서비스를 많은 사람들이 이용한다면
엄청나게 느려질 것이다.


해결

스택오버플로우에서 제시한 해결 방안은
쿼리를 두개로 나누자는 것이었다.

먼저 1 에 해당하는 엔티티의 주식별자들을 페이지네이션으로 가져온다

List<Long> ids = queryFactory.selectFrom(QA.a)
			.offset(page.getOffset())
            .limit(page.getLimit())
            .fetch();

그후 IN절을 사용해 조인 쿼리를 만든다.

List<A> aList = queryFactory.selectFrom(qA)
                .leftJoin(a.b, QB.b)
                .fetchJoin()
                .leftJoin(a.c, QC.c)
                .fetchJoin()
                .leftJoin(a.d, QD.d)
                .fetchJoin()
                .where(a.id.in(ids)) // 위에서 추출한 id들
                .fetch();

쿼리를 날리는 수는 하나 많아졌지만 필요한 만큼의 데이터만 가져올 수 있게 됐다.


결론

fetch join 과 pagination 을 같이 사용하면 자바상에서 데이터에 대한 페이지네이션을 처리하기 때문에 다른 방법을 찾아보자.

profile
아직 배우는 중입니다

0개의 댓글