JOIN FETCH가 N+1보다 느릴 수 있다: 재현 가능한 Doctrine 벤치마크 (+ 엔티티당 1행 JSON 집계)
발행: (2025년 12월 18일 오후 02:54 GMT+9)
3 min read
원문: Dev.to
Source: Dev.to
실제 문제: 다중 OneToMany JOIN이 행을 폭발시킴
여러 OneToMany 관계를 JOIN 하면 SQL 결과 집합이 곱해집니다(카르테시안 곱).
예시
- 이미지 3개 × 리뷰 5개 = 제품당 15행
- 제품 2000개 → ~30,000행이 DB에서 전송되고 처리됨
Doctrine의 identity map은 PHP에서 중복을 숨겨 주지만, DB는 여전히 곱해진 행 집합을 반환합니다. 쿼리 수가 적어도 성능에 큰 타격을 줄 수 있습니다.
링크 (패키지 + 재현 가능한 벤치마크)
- 벤치마크 저장소: https://github.com/rgalstyan/doctrine-aggregated-queries-benchmark
- 번들 저장소: (원문에 링크 생략)
재현 가능한 벤치마크 (Symfony + PostgreSQL)
필요한 고정 데이터와 CLI 명령을 포함한 독립 실행형 Symfony 벤치마크 프로젝트를 공개했습니다. 누구나 로컬에서 결과를 재현할 수 있습니다:
https://github.com/rgalstyan/doctrine-aggregated-queries-benchmark
샘플 실행 (limit = 2000)
참고: 시간은 머신/DB/캐시 상태에 따라 달라집니다. 중요한 것은 추세입니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PRODUCTS PERFORMANCE TEST
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dataset size: 2000 products
TRADITIONAL DOCTRINE (2000 records)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Time: 327.73ms
Memory: 44559.6 KB (43.52 MB)
Queries: 43
Result: 2000 Product entities
DOCTRINE JOIN FETCH (entities) (2000 records)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Time: 816.37ms
Memory: 39795.9 KB (38.86 MB)
Queries: 2
DB rows: ~30000 (Cartesian product in SQL)
Result: 2000 Product entities
SIMPLE JOINS (naive) (2000 records)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Time: 103.14ms
Memory: 11659.4 KB (11.39 MB)
Queries: 1
DB rows: 30000 (Cartesian product!)
Result: 2000 products (after deduplication)
AGGREGATED QUERIES (2000 records)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Time: 66.18ms
Memory: 13989.1 KB (13.66 MB)
Queries: 1
DB rows: 2000
Result: 2000 products (arrays)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
COMPARISON
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
비교 표
| 접근 방식 | 반환 | 시간 (ms) | 메모리 (KB) | 쿼리 수 | DB 행 수 | 제품 수 |
|---|---|---|---|---|---|---|
| 전통적인 Doctrine | entities | 327.73 | 44559.6 | 43 | N/A | 2000 |
| Doctrine JOIN fetch | entities | 816.37 | 39795.9 | 2 | 30000 | 2000 |
| 단순 JOIN (naive) | arrays | 103.14 | 11659.4 | 1 | 30000 | 2000 |
| JSON 집계 | arrays | 66.18 | 13989.1 | 1 | 2000 | 2000 |