inflearn logo
강의

講義

知識共有

本当のjOOQ! Type Safe SQL with Java

Conditionを活用した簡潔な検索条件の作成

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

60

JaeWon Kim

投稿した質問数 10

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

回答 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