[์Šคํ”„๋ง] JPA N+1 ๋ฌธ์ œ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

2025. 3. 27. 11:34ยท๐ŸŽฏ Programming
๋ฐ˜์‘ํ˜•
์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋งŒ๋‚ฌ๋˜ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์„ ์ค‘์‹ฌ์œผ๋กœ ์ž‘์„ฑํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

โœ… N+1 ๋ฌธ์ œ๋ž€?

: ์š”์ฒญ์ด 1๊ฐœ์˜ ์ฟผ๋ฆฌ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธธ ๊ธฐ๋Œ€ํ–ˆ๋Š”๋ฐ ์ถ”๊ฐ€๋กœ N๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ

 

๊ฐœ๋…์€ ์ •๋ง ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

N๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ 1๊ฐœ๋ฅผ ๋‚ ๋ ธ๋Š”๋ฐ, ์ถ”๊ฐ€์ ์œผ๋กœ N๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋” ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

(์‚ฌ์‹ค ํ๋ฆ„์ƒ 1+N ๋ฌธ์ œ๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค)

 

์ด์ œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ์„ ์ค‘์‹ฌ์œผ๋กœ `N+1` ๋ฌธ์ œ๊ฐ€ ์–ธ์ œ, ์–ด๋–ค ์ƒํ™ฉ์—์„œ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 


โœ… ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ

๐Ÿ“ ERD

"ํ”„๋กœ์ ํŠธ ํŒ€์›์„ ๋ชจ์ง‘ํ•˜๋Š” ๊ธฐ๋Šฅ"์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด `ERD`๋ฅผ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

ERD
ํ”„๋กœ์ ํŠธ ํŒ€์› ๋ชจ์ง‘ ๊ด€๋ จ ERD

 

ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ์—๋Š” ์„ ํ˜ธํ•˜๋Š” ์„ฑ๊ฒฉ, ๋ชจ์ง‘ํ•˜๋Š” ๋ถ„์•ผ, ๋ชจ์ง‘ํ•˜๋Š” ์–ธ์–ด๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ๊ฐ 1:N์˜ ๊ด€๊ณ„๋กœ ์—ฐ๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ: ์„ ํ˜ธ ์„ฑ๊ฒฉ = `1:N`
  • ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ: ๋ถ„์•ผ = `1:N`
  • ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ: ์–ธ์–ด = `1:N`

 

๐Ÿ“ Entity

์œ„ ERD์— ๋”ฐ๋ผ ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ(RecruitPost) ์—”ํ‹ฐํ‹ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

RecruitPost Entity
RecruitPost Entity

๋กœ์ง ๊ตฌํ˜„ ๊ณผ์ •์—์„œ `RecruitPost` -> `RecruitField`, `RecruitLanguage`, `RecruitPersonality` ๋ฐฉํ–ฅ์ด ์ž์ฃผ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜์–ด ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๊ฐ๊ฐ์˜ ๊ด€๊ณ„๋Š” ์ง€์—ฐ ๋กœ๋”ฉ(`FetchType.LAZY`)์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

์ง€์—ฐ ๋กœ๋”ฉ: ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฒ˜์Œ์—๋Š” ์กฐํšŒํ•˜์ง€ ์•Š๊ณ , ์‹ค์ œ๋กœ ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ•„์š”ํ•œ ์‹œ์ ์— ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹

 

๐Ÿ“ Service & Repository

`N+1` ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž์—๊ฒŒ ์ ์ ˆํ•œ ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ์„ ์ถ”์ฒœํ•ด ์ฃผ๋Š” ๋กœ์ง์ž…๋‹ˆ๋‹ค.

 

๋จผ์ € JpaRepository์˜  `findAllByIsCompleteAndUserIdNot(false, user)`๋ฅผ ํ†ตํ•ด

ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ชจ๋“  ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•ด์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  `calculatePriorities()`์—์„œ ์ด ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋“ค(์–ธ์–ด, ๊ด€์‹ฌ๋ถ„์•ผ, ์„ฑ๊ฒฉ)์„ `get`ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

business logic
calculate method


โœ… ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๐Ÿ“ ๋กœ์ง ์‹คํ–‰ ๊ฒฐ๊ณผ

์œ„ ๋กœ์ง์„ ์‹คํ–‰ํ•˜๋ฉด, ์ฐํžˆ๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

N+1 query

์—ฌ๊ธฐ์„œ `N`์€ ํ˜„์žฌ DB์— ๋“ค์–ด์žˆ๋Š” ํŒ€์› ๋ชจ์ง‘ ๊ฒŒ์‹œ๋ฌผ RecruitPost์˜ ๊ฐœ์ˆ˜์ž…๋‹ˆ๋‹ค.

 

`RecruitPost` ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ 1๋ฒˆ + ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ `RecruitPost`์™€ ์—ฐ๊ด€๋œ ์–ธ์–ด, ๋ถ„์•ผ, ์„ฑ๊ฒฉ์„ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ N๋ฒˆ

= `1+N`

= `N+1`

 

์ œ๊ฐ€ JpaRepository์˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ฒƒ์€ 1๋ฒˆ์ž„์—๋„, ์ถ”๊ฐ€๋กœ N๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋” ๋ฐœ์ƒํ•œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ์ƒํ™ฉ์ด ๋ฐ”๋กœ N+1 ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

 

๐Ÿ“ N+1 ๋ฌธ์ œ ๋ฐœ์ƒ

1+3N query

 

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ์–ด๋–ค ๋ถ€๋ถ„์—์„œ ์ด ์ฟผ๋ฆฌ๋“ค์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๋ฉด ์œ„์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ `RecruitPost`์™€ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ 3๊ฐœ(์–ธ์–ด, ๋ถ„์•ผ, ์„ฑ๊ฒฉ)์ด๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•˜๊ฒŒ๋Š” `1+3N`๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๐Ÿ“์›์ธ ๋ถ„์„

JPQL: ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ

 

Spring Data Jpa์—์„œ๋Š” ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ ๋ถ„์„ํ•ด์„œ `JPQL`์„ ์ƒ์„ฑํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ๋ฉ”์„œ๋“œ๋ช…์„ ํ†ตํ•ด WHERE ์กฐ๊ฑด์ ˆ์„ ๋งŒ๋“ค๊ณ , `FROM RecruitPost`๋กœ ๊ฒŒ์‹œ๋ฌผ์„ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

before method
๊ธฐ์กด ๋ฉ”์„œ๋“œ

 

๊ทธ๋Ÿฐ๋ฐ, ์ด์ „์— ๋ดค๋˜ `RecruitPost` ํด๋ž˜์Šค ์•ˆ์—๋Š” `RecruitField`, `RecruitLanguage`, `RecruitPersonality`์˜ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ด ๋ฆฌ์ŠคํŠธ๋“ค๋„ ์œ„ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ฐ™์ด ์กฐํšŒํ•ด ์˜ฌ๊นŒ์š”?

 

 

์œ„ ๋ฆฌ์ŠคํŠธ๋“ค์€ RecruitPost ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•ด ์˜ฌ ๋•Œ ํ•จ๊ป˜ ์‹ค์ œ DB์—์„œ ์กฐํšŒํ•ด์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 

 

์™œ๋ƒํ•˜๋ฉด "์ง€์—ฐ ๋กœ๋”ฉ"์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ์š”!

 ์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด RecruitPost๋ฅผ ์กฐํšŒํ•  ๋•Œ๋Š” ๋ฆฌ์ŠคํŠธ๋“ค์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ์กด์žฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

์ดํ›„ ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด post.getXXX() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ๊ทธ ์‹œ์ ์— ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. 

 


โœ… ํ•ด๊ฒฐ ๊ณผ์ •

์ด์ œ ํ—˜๋‚œํ–ˆ๋˜ ํ•ด๊ฒฐ ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

๊ณผ๊ฑฐ ๊น€์˜ํ•œ ๋‹˜ ๊ฐ•์˜์—์„œ N+1 ๋ฌธ์ œ์— ๋Œ€ํ•ด ํ•™์Šตํ•œ ๊ฒฝํ—˜์ด ์žˆ๊ธฐ์—, ๊ธˆ๋ฐฉ ํ•ด๊ฒฐ๋  ์ค„ ์•Œ์•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค..

 

๐Ÿ“ JPQL์˜ FETCH JOIN ์‚ฌ์šฉ & @EntityGraph ์‚ฌ์šฉ

`FETCH JOIN`์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ™์ด ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ด€๋ จ๋œ 3๊ฐœ์˜ ์—”ํ‹ฐํ‹ฐ์— ๋ชจ๋‘ `FETCH JOIN`์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ๊ฐ™์ด ์กฐํšŒํ•ด์˜ค๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

fetch join method
(๋ณ€๊ฒฝ) Fetch Join ์‚ฌ์šฉ

 

๊ทธ๋Ÿฌ๋‚˜, ์‹คํ–‰ํ•ด ๋ณด๋‹ˆ  "Hibernate MultipleBagFetchException"์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์ฒ˜์Œ์—๋Š” `JPQL`์˜ `FETCH JOIN`์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ์ธ๊ฐ€? ์ƒ๊ฐํ•˜๊ณ  `@EntityGraph`๋กœ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

entity graph method
(๋ณ€๊ฒฝ) Entity Graph ์‚ฌ์šฉ

 

`@EntityGraph`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋œ ์—ฐ๊ด€๊ด€๊ณ„ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ํ•œ ๋ฒˆ์— (Left Join์œผ๋กœ) ์กฐํšŒํ•ด ์˜ต๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ "Hibernate MultipleBagFetchException"์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

 

๐Ÿ“ MultipleBagFetchException

: 2๊ฐœ ์ด์ƒ์˜ `OneToMany` ์ž์‹ ํ…Œ์ด๋ธ”์— `FETCH JOIN`ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ

 

์ œ๊ฐ€ OneToMany ๊ด€๊ณ„๋ฅผ ๋ชจ๋‘ List๋กœ ์„ ์–ธํ•ด ๋‘์—ˆ๋Š”๋ฐ, Hibernate๋Š” ์ด๋ฅผ `Bag`๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

 

๋‘ ๊ฐœ ์ด์ƒ์˜ `Bag`๋ฅผ ํ•จ๊ป˜ `Fetch Join`ํ•˜๋ฉด, ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Hibernate๋Š” ์ด ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—, MultipleBagFetchException์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์ฆ‰, 2๊ฐœ ์ด์ƒ์˜ `@OneToMany` ๊ด€๊ณ„์—์„œ๋Š” fetch join์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค!

 

์ด ์˜ˆ์™ธ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” 2๊ฐ€์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

(1) List๋ฅผ Set์œผ๋กœ ๋ณ€๊ฒฝ

(2) fetch size ์„ค์ •

 

 

๐Ÿ“ MultipleBagFetchException ํ•ด๊ฒฐ: List -> Set ๋ณ€๊ฒฝ

`List`๋Š” ์ค‘๋ณต์˜ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์—ˆ์ง€๋งŒ, `Set`์€ ์ค‘๋ณต ๋ฐ์ดํ„ฐ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•„๋„ ๋˜๋ฏ€๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด `RecruitPost` ์—”ํ‹ฐํ‹ฐ ๋‚ด๋ถ€์—์„œ ๋ชจ๋‘ `Set`์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

to set
List -> Set ๋ณ€๊ฒฝ

 

 

๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰ํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฟผ๋ฆฌ๊ฐ€ ์ฐํž™๋‹ˆ๋‹ค.

fetch join query
Fetch Join ๊ฒฐ๊ณผ

 

์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํ†ตํ•ด `Fetch Join`ํ•˜์—ฌ ํ•œ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ ๋ชจ๋‘ ์กฐํšŒํ•ด ์˜ค๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ Set์œผ๋กœ ๋ณ€๊ฒฝํ•จ์— ๋”ฐ๋ผ ์ด๋ฏธ ์ž‘์„ฑํ•ด ๋‘” ๋กœ์ง์—์„œ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๐Ÿ“ MultipleBagFetchException ํ•ด๊ฒฐ:  batch size ์„ค์ •

batch size: ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ช‡ ๊ฐœ์”ฉ ๊ฐ€์ ธ์˜ฌ์ง€ ์„ค์ • (IN์ ˆ์— ๋“ค์–ด๊ฐˆ ๊ฐ’ ๊ฐœ์ˆ˜๋ฅผ ์„ค์ •)

 

batch size๋ฅผ ์ ์šฉํ•˜๋ฉด, WHERE์ ˆ์ด ๊ฐ™์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ SELECT ์ฟผ๋ฆฌ๋“ค์„ ํ•˜๋‚˜์˜ IN ์ฟผ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ ์–ด๋ ค์šด ์ ์€ batch size๋Š” ์ƒํ™ฉ์— ์ ์ ˆํ•˜๊ฒŒ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ณดํ†ต 100~1000์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋ผ๊ณ  ๋ณด๊ธด ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ, ๋” ์ฐพ์•„๋ณด๋‹ˆ batch size ์บ์‹ฑ ์ผ€์ด์Šค ์ตœ์ ํ™”๋กœ ์ธํ•ด (batch size!= IN์ ˆ์— ๋“ค์–ด๊ฐ„ ๊ฐ’ ๊ฐœ์ˆ˜)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์€ ์ถ”ํ›„์— ๋” ์ฐพ์•„์„œ ๋”ฐ๋กœ ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

batch size๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. ํ”„๋กœ์ ํŠธ ์ „์—ญ์— ๋ฐฐ์น˜ ์‚ฌ์ด์ฆˆ ์ ์šฉ

default batch size
application.yml์— ์ถ”๊ฐ€

2. ๊ฐ๊ฐ ๋ฐฐ์น˜ ์‚ฌ์ด์ฆˆ ์ ์šฉ

recruitpost batch size
RecruitPost ์ˆ˜์ •

 


โœ… ํ•ด๊ฒฐ๋ฐฉ๋ฒ• ์ •๋ฆฌ

๐Ÿ“ OneToMany ๊ด€๊ณ„๊ฐ€ ํ•˜๋‚˜์ผ ๋•Œ

1. JPQL Fetch Join

2. @EntityGraph

3. Querydsl Fetch Join (Querydsl์—์„œ๋„ `fetchJoin()`์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

=> ํ•˜์ง€๋งŒ, ์—ฐ๊ด€๋œ ๊ฒƒ์ด  2๊ฐœ ์ด์ƒ์ด๋ฉด ์œ„ 3 ๋ฐฉ์‹ ๋ชจ๋‘ MultipleBagFetchException ๋ฐœ์ƒ

 

๐Ÿ“ OneToMany ๊ด€๊ณ„๊ฐ€ 2๊ฐœ ์ด์ƒ์ผ ๋•Œ

1. List -> Set ๋ณ€๊ฒฝ

2. Batch Size ์„ค์ •

๐Ÿ“ ๊ฐœ์ธ์  ๊ฒฐ๋ก 

  • Fetch Join์˜ ๋ฌธ์ œ์ : ํŽ˜์ด์ง•์ด ์•ˆ ๋˜๊ณ , OneToMany๊ด€๊ณ„์—์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์นดํ…Œ์‹œ์•ˆ ๊ณฑ๋งŒํผ ๋Š˜์–ด๋‚˜์„œ ์กฐํšŒ๋œ๋‹ค
  • ๋”ฐ๋ผ์„œ OneToMany ๊ด€๊ณ„์—์„œ์˜ N+1 ๋ฌธ์ œ์—์„œ๋Š” ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ํ•œ ๊ฐœ์ด๋“  ๋‘ ๊ฐœ์ด๋“  ํŽ˜์ด์ง•์ด ๊ฐ€๋Šฅํ•˜๊ณ  ์ตœ์†Œ ์„ฑ๋Šฅ์„ ๋ณด์žฅํ•ด ์ฃผ๋Š” batch size๋ฅผ ์‚ฌ์šฉํ•˜์ž

 

์นดํ…Œ์‹œ์•ˆ ๊ณฑ (Cartesian Product): N๊ฐœ์˜ ํ–‰์„ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”๊ณผ M๊ฐœ์˜ ํ–‰์„ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์„ Join ํ•˜๋ฉด, N*M๊ฐœ์˜ ๊ฒฐ๊ด๊ฐ’์ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ
๋ฐ˜์‘ํ˜•

'๐ŸŽฏ Programming' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[์Šคํ”„๋ง] ์Šคํ”„๋ง ์ด๋ฒคํŠธ๋Š” ์–ธ์ œ ์จ์•ผํ• ๊นŒ?  (2) 2025.04.02
[ISSUE] error: connect econnrefused  (1) 2025.02.17
[JAVA] ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐฉ๋ฒ•(+์ •๋ ฌ)  (1) 2025.01.31
[Github Action] build ์˜ค๋ฅ˜ ์›์ธ ๋” ์ž์„ธํžˆ ๋ณด๋Š” ๋ฒ•  (0) 2024.10.08
'๐ŸŽฏ Programming' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [์Šคํ”„๋ง] ์Šคํ”„๋ง ์ด๋ฒคํŠธ๋Š” ์–ธ์ œ ์จ์•ผํ• ๊นŒ?
  • [ISSUE] error: connect econnrefused
  • [JAVA] ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐฉ๋ฒ•(+์ •๋ ฌ)
  • [Github Action] build ์˜ค๋ฅ˜ ์›์ธ ๋” ์ž์„ธํžˆ ๋ณด๋Š” ๋ฒ•
dev-heyjin
dev-heyjin
  • dev-heyjin
    ๊ฐœ๋ฐœ ๊ธฐ๋ก
    dev-heyjin
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (56)
      • ๐ŸŽฏ Programming (8)
      • ๐Ÿ’ช Algorithm (16)
      • โš™๏ธ CS (31)
        • ๋„คํŠธ์›Œํฌ (15)
        • ์šด์˜์ฒด์ œ (15)
        • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค (0)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    DB
    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
    ํ•ดํ‚น
    RDS
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
dev-heyjin
[์Šคํ”„๋ง] JPA N+1 ๋ฌธ์ œ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”