inflearn logo
강의

Course

Instructor

jOOQ in action! Type Safe SQL with Java

Creating simple search conditions using Condition

안녕하세요 FetchGroups에서 Multiset방식 사용하면 어떤가 해서 물어봅니다

60

JaeWon Kim

10 asked

0

안녕하세요 공부하다가 보니

FetchGroups방식으로 했을때 Map으로 반환되고 DTO로 변환해야하는 오버헤드가 발생하는데요

Multiset방식으로 하면 1:N 관계에서 바로 DTO로 반환 할 수 있는데요

코드 보기가 조금은 더 복잡해 보이지만 WHERE절이 있을때 현재 테이블 데이터와 인덱스 상으로는 성능적으로 더 괜찮은거 같아서 multiset방식은 강의에 없어서 어떤지 궁금해서 여쭤봅니다

 

 

public List<ActorFilmography> findActorFilmographyWithMultiset(ActorFilmographySearchOption searchOption) {
    final JFilmActor FILM_ACTOR = JFilmActor.FILM_ACTOR;
    final JFilm FILM = JFilm.FILM;

    return dslContext.select(
                    ACTOR,
                    DSL.multiset(
                            DSL.select(FILM.fields())
                                    .from(FILM)
                                    .join(FILM_ACTOR)
                                    .on(FILM.FILM_ID.eq(FILM_ACTOR.FILM_ID))
                                    .where(
                                            FILM_ACTOR.ACTOR_ID.eq(ACTOR.ACTOR_ID),
                                            containsIfNotBlank(FILM.TITLE, searchOption.getFilmTitle())
                                    )
                    ).convertFrom(r -> r.into(Film.class))
            )
            .from(ACTOR)
            .where(
                    containsIfNotBlank(ACTOR.FIRST_NAME.concat(" ").concat(ACTOR.LAST_NAME), searchOption.getActorName())
            )
            .fetch(record -> new ActorFilmography(
                    record.value1().into(Actor.class),
                    record.value2()
            ));
}

WHERE 조건 포함 시 성능 비교

fetchGroups 방식 (cost=405, rows=609)

Film 필터링 먼저 → FilmActor 조인 → Actor 필터링

1. film 테이블 스캔 (1000 rows)

└─ LIKE '%COMMANDMENTS EXPRESS%' → 111 rows

2. film_actor 인덱스 조회 (idx_fk_film_id) → 609 rows

3. actor PRIMARY KEY 조회 → 609 rows

└─ LIKE '%LOLLOBRIGIDA%' → 1 row (최종)

특징:

- Film 조건이 선택적이면 먼저 필터링하여 조인 범위 축소

- 하지만 Actor 필터링은 마지막에 수행 (비효율)

- 609개 row가 네트워크 전송 → 애플리케이션에서 1개로 그룹핑

---

multiset 방식 (cost=20.2 + 13.3 × filtered_actors)

Actor 필터링 먼저 → 각 Actor의 Film 서브쿼리

1. actor 테이블 스캔 (200 rows)

└─ LIKE '%LOLLOBRIGIDA%' → 1 row (필터링 후)

2. 필터링된 1개 actor에 대해 서브쿼리 실행:

- film_actor PRIMARY KEY 조회 (27.3 rows 추정)

- film PRIMARY KEY 조회 (27.3번)

- LIKE '%COMMANDMENTS EXPRESS%' → 1개 매칭

특징:

- Actor 조건을 먼저 적용하여 서브쿼리 실행 횟수 최소화

- 필터링된 1개 Actor만 네트워크 전송 (JSON 포함)

- Film 필터링은 각 actor 서브쿼리 내부에서 수행

---

실제 Total Cost 계산

fetchGroups

Total: 405

네트워크 전송: 609 rows (Actor + Film 중복 포함)

애플리케이션 처리: Map 생성 + Stream 변환

multiset

Total: 20.2 + (13.3 × 1) = 33.5

네트워크 전송: 1 row (Actor + JSON array)

애플리케이션 처리: JSON 역직렬화

결론: WHERE 조건이 있으면 multiset이 압도적으로 유리 (405 vs 33.5)

FetchGroups는 Actor를 나중에 필터링하고 multiset은 actor를 먼저 필터링해서 1개에 대한 film을 여러개 가져와서 현재 데이터에서는 더 유리한데 일반적인 사용법이 궁금합니다

java sql spring-boot jooq dsl

Answer 1

0

communityai8509

안녕하세요, 인프런 AI 인턴입니다.

JaeWon Kim님, Multiset을 사용하여 1:N 관계에서 데이터베이스 쿼리 성능을 향상시키고 결과를 DTO로 쉽게 매핑할 수 있는 방법에 대해 문의하신 점 잘 이해했습니다. Multiset 방식을 활용하면, 각 Actor에 대해 관련된 Film 정보를 서브쿼리를 통해 바로 가져올 수 있기 때문에, 특히 큰 데이터셋에서 성능 이점이 있을 수 있습니다.

Multiset 방식과 FetchGroups 방식을 비교하자면:

  1. 효율성: Multiset 방식은 Actor에 즉시 필터링을 적용하고, 관련된 데이터를 서브쿼리로 가져오기 때문에 필요한 데이터 양이 줄어듭니다. 반면, FetchGroups는 조인이 완료된 후에 필터링이 진행되어 필요 없는 데이터를 더 많이 전달하고 후처리합니다.

  2. 네트워크 전송량: Multiset 방식은 적은 양의 데이터를 네트워크를 통해 전송하므로 대용량 데이터 처리에 유리합니다. 특히, 네트워크 전송 비용이 높은 경우 명확한 이점이 있습니다.

  3. 코드 복잡성: Multiset 방식이 초기에는 다소 복잡하고 생소하게 느껴질 수 있지만, 익숙해지면 충분히 효율적인 코딩 패턴이 될 수 있습니다. 코드의 복잡성은 결국 개발자의 숙련도에 따라 다르게 느껴질 수 있습니다.

  4. 유연성: Multiset은 다양한 데이터 관계를 한 번의 쿼리로 쉽게 매핑할 수 있는 장점을 제공합니다. 특히 DTO로 직접 매핑하여 애플리케이션에서 추가 처리 없이 사용할 수 있습니다.

강의에 포함되지 않았던 이유는, 현실의 데이터베이스 스키마나 비즈니스 요구사항에 따라 모든 케이스에서 Multiset 방식이 더 나은 선택이 아닐 수도 있기 때문입니다. 특정 상황에서는 FetchGroups가 더 직관적이고 관리가 용이할 수 있습니다.

다음 링크에서 더 많은 정보를 참고하실 수 있습니다:
- jOOQ의 Multiset 사용법 공식 문서
- 병렬 처리와 네트워크 비용 분석에 관한 기초

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

monosoul jooq plugin-docker registry 변경

1

101

3

질문) jOOQ 프로젝트에서 CI/CD 자동화 문의

1

176

3

generate dao 를 통한 삽입 시 pk auto-increment 가 적용되지 않습니다

1

109

1

jooq+flyway에서 질문드립니다

0

250

2

안녕하세요 생성일,생성자,수정일,수정자는 자동으로 해주는 기능이 있을까요?

0

209

2

[사례] Maven에서 TestContainers 플러그인 적용시 예외발생

0

143

0

Routines 관련 질문

1

159

2

JOOQ 환경설정 중에 db 패스워드 관련 질문입니다.

1

173

1

Sakila DB 도커 컴포즈 구성 시 팁

1

152

1

DB 벤더별 내장함수에 대한 방언 지원 듣고 질문드려요

1

176

1

인텔리제이 테마 질문

2

426

3

kotlin jOOQ , 컴파일시 힙메모리 부족

0

525

2

pojo 에 setter 가 없는 경우가 있을까요?

1

189

1

FilmWithActor로 fetch 시 select와 생성자의 매개변수 순서가 다를 경우 map이 정상적으로 이루어 지지않는 케이스

1

228

1

from절 subquery table filed nullable 처리

1

221

1

kotlin mapping error

0

517

4

db column은 not null로 되어 있는데 kotlin에서 pojo가 다 nullable로 생성되요.

0

514

1

하나의 디비를 여러 백엔드 서버가 필요로 할 때, flyway 관리

1

889

5

dslContext 작성 시 fetchGroups 이렇게 작성해도 될까요?

2

639

6

update with dto 질문

1

268

1

R2DBC 환경에서 jOOQ generate 된 Dao를 사용할 수 있는 방법이 있을까요?

2

1245

3

IDE로 스프링부트 실행없이 jooq로 짠 쿼리 결과를 바로 확인하는 방법이 있을까요?

1

503

1

build.gradle.kts

3

758

2

마리아 DB 쓰시는분들은 mysql 파일 쓰시면됩니다.

1

371

1