์ฑ„๋„ํ†ก ์•„์ด์ฝ˜

๊ณ ์„ฑ๋Šฅ Spring Persistence (High-Performance Spring Persistence)

ํŽ˜์น˜ ์กฐ์ธ๊ณผ batch_fetch_size ์ดํ›„, ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„์˜ JPA ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฐ•์˜์ž…๋‹ˆ๋‹ค. JPA ๋„ˆ๋จธ Hibernate ยท JDBC ยท DB ์—”์ง„๊นŒ์ง€ โ€” ์ˆ˜๋ฉด ์•„๋ž˜ ๋ ˆ์ด์–ด์˜ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ , ์„ค์ • ํ•œ ์ค„๋กœ 18๋ฐฐ ์„ฑ๋Šฅ ์ฐจ์ด๋ฅผ ๋งŒ๋“œ๋Š” 7๊ฐ€์ง€ ์ „๋žต์„ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค. ์กฐํšŒ(Read) ์ตœ์ ํ™”์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ , ์“ฐ๊ธฐ(Write) ์„ฑ๋Šฅ๊ณผ ์—ฐ๊ด€๊ด€๊ณ„ ํ•จ์ •, ํ”„๋กœ๋•์…˜ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ๊นŒ์ง€ Spring Persistence ์ „์ฒด๋ฅผ ํŠœ๋‹ํ•ฉ๋‹ˆ๋‹ค. Java Champion์ด์ž Hibernate ํ•ต์‹ฌ ๊ธฐ์—ฌ์ž์ธ Vlad Mihalcea๊ฐ€, Spring Data JPA์˜ ํ•œ๊ณ„๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ง์ ‘ ๋งŒ๋“  ๋„๊ตฌ์™€ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐ€๋ฅด์นฉ๋‹ˆ๋‹ค.

20๋ช… ์ด ์ˆ˜๊ฐ•ํ•˜๊ณ  ์žˆ์–ด์š”.

๋‚œ์ด๋„ ์ค‘๊ธ‰์ด์ƒ

์ˆ˜๊ฐ•๊ธฐํ•œ ๋ฌด์ œํ•œ

์ˆ˜๊ฐ• ํ›„ ์ด๋Ÿฐ๊ฑธ ์–ป์„ ์ˆ˜ ์žˆ์–ด์š”

  • ๋ถˆํ•„์š”ํ•œ SELECT๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ๋Œ€๋Ÿ‰ INSERT ์„ฑ๋Šฅ์„ 10๋ฐฐ ์ด์ƒ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ตํž™๋‹ˆ๋‹ค.

  • ์ˆ˜๋ฐฑ๋งŒ ๊ฑด์˜ ๋ฐ์ดํ„ฐ์—์„œ๋„ ํŽ˜์ด์ง€ ์ˆ˜์— ๊ด€๊ณ„์—†์ด ์ผ์ •ํ•œ ์กฐํšŒ ์†๋„๋ฅผ ์œ ์ง€ํ•˜๋Š” ์ „๋žต์„ ๋ฐฐ์›๋‹ˆ๋‹ค

  • ์„ค์ • ํ•œ ์ค„์˜ ์ฐจ์ด๊ฐ€ ๋งŒ๋“œ๋Š” 18๋ฐฐ ์„ฑ๋Šฅ ๊ฒฉ์ฐจ๋ฅผ, 4๊ฐœ DB ๋ฒค์น˜๋งˆํฌ ์ˆ˜์น˜๋กœ ์ง์ ‘ ์ฒด๊ฐํ•ฉ๋‹ˆ๋‹ค.



๊ธ€๋กœ๋ฒŒ ์ž๋ฐ”์ฑ”ํ”ผ์–ธ์—๊ฒŒ ๋ฐฐ์šฐ๋Š”

๊ณ ์„ฑ๋Šฅ ์Šคํ”„๋ง Persistence ๊ฐ•์˜


๊ณ ์„ฑ๋Šฅ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์€, ๊ทธ ์•„๋ž˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ˜ธํก์ด ๋งž์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
JPA ๋„ˆ๋จธ Hibernate ยท JDBC ยท DB๊นŒ์ง€ โ€” ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต ์ „์ฒด๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ์—ฌ์ •์ž…๋‹ˆ๋‹ค.


ํ˜น์‹œ ์ด๋Ÿฐ ๊ฒฝํ—˜, ์žˆ์œผ์‹ ๊ฐ€์š”?


โ†’ JpaRepository.saveAll()์ด ์—”ํ‹ฐํ‹ฐ ์ˆ˜๋งŒํผ ๋ถˆํ•„์š”ํ•œ SELECT๋ฅผ ๋‚ ๋ฆฌ๋Š” ๊ฑธ ๋ฐœ๊ฒฌํ•œ ์  ์žˆ๋‚˜์š”?

โ†’ @DynamicUpdate๋ฅผ ์ผฐ๋”๋‹ˆ ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง„ ๊ฒฝํ—˜์€์š”?

โ†’ OFFSET ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜์ด ๋’ค๋กœ ๊ฐˆ์ˆ˜๋ก 20๋ฐฐ ๋А๋ ค์ง€๋Š” ์›์ธ์„ ๋ชจ๋ฅด๊ฒ ๋‹ค๋ฉด?

โ†’ 10๋งŒ ๊ฑด ์กฐํšŒ ์‹œ ์„ค์ • ํ•œ ์ค„ ์ฐจ์ด๋กœ 550ms โ†’ 30ms (18๋ฐฐ ๊ฐœ์„ )๊ฐ€ ๊ฐ€๋Šฅํ•œ ๊ฑธ ์•„์‹œ๋‚˜์š”?

JPA๋Š” ๋น™์‚ฐ์˜ ์ผ๊ฐ
ํ•ต์‹ฌ์€ '์ˆ˜๋ฉด ์•„๋ž˜'


๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์ด DB์™€ ํ˜ธํก์„ ๋งž์ถ”๋ ค๋ฉด,
์ˆ˜๋ฉด ์•„๋ž˜ Hibernate ยท JDBC ยท ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—”์ง„์˜ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ์ฐจ์ด๊ฐ€ ๊ณ ์„ฑ๋Šฅ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ, ๊ฒจ์šฐ ๋Œ์•„๊ฐ€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐ€๋ฆ…๋‹ˆ๋‹ค.



๊ฐ•์˜์—์„œ ๋ฐฐ์šฐ๋Š” ๋‚ด์šฉ

์Šคํ”„๋ง ์„ฑ๋Šฅ์„ ์žก๋Š” 7๊ฐ€์ง€ ์ตœ์ ํ™” ์ „๋žต


์—”ํ‹ฐํ‹ฐ ๋งคํ•‘๋ถ€ํ„ฐ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ, ์—ฐ๊ด€๊ด€๊ณ„ ํ•จ์ •, ์ง€์—ฐ ๋กœ๋”ฉ, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ๊นŒ์ง€
โ€” Spring Persistence ์„ฑ๋Šฅ ํŠœ๋‹์˜ ํ•ต์‹ฌ ์ „๋žต์„ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.


์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•œ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ๊ณผ ์‹๋ณ„์ž ์„ค๊ณ„

๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด @Id ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๋Š” ๊ฒƒ์—๋งŒ ์ง‘์ค‘ํ•˜์ง€๋งŒ, ์–ด๋–ค ์‹๋ณ„์ž ์ „๋žต์„ ์„ ํƒํ•˜๋Š”์ง€๊ฐ€ ์“ฐ๊ธฐ ์„ฑ๋Šฅ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. save()๊ฐ€ ์ƒˆ ์—”ํ‹ฐํ‹ฐ์ธ๋ฐ๋„ SELECT๋ฅผ ๋‚ ๋ฆฌ๋Š” ์ด์œ , ํŠน์ • ID ์ „๋žต์ด ๋ฐฐ์น˜๋ฅผ ๋ง‰๋Š” ์ด์œ ๋ฅผ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

Before

ํ”ํžˆ ์“ฐ๋Š” ID ์ „๋žต + save()
โ†’ ๋ฐฐ์น˜ INSERT ๋ถˆ๊ฐ€, ์—”ํ‹ฐํ‹ฐ๋‹น 1๊ฑด์”ฉ INSERT

After

ID ์ „๋žต๊ณผ ์ €์žฅ ๋ฐฉ์‹ ๋ณ€๊ฒฝ์œผ๋กœ
โ†’ JDBC ๋ฐฐ์น˜๋กœ ๋Œ€๋Ÿ‰ INSERT ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ


์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋™์ž‘ ์›๋ฆฌ์™€ Flush ๋ฉ”์ปค๋‹ˆ์ฆ˜

"์˜๋„์น˜ ์•Š์€ UPDATE ์ฟผ๋ฆฌ๊ฐ€ ์™œ ์ด๋ ‡๊ฒŒ ๋งŽ์ง€?" โ€” 'SQL์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ '์„ ๋” ์ด์ƒ ์ถ”์ธก์— ๋งก๊ธฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Dirty Checking์˜ ๋‚ด๋ถ€ ๋™์ž‘์„ ๋ถ„์„ํ•˜๊ณ , @DynamicUpdate์˜ ์ง„์งœ ์‚ฌ์šฉ ๊ธฐ์ค€์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

Before

@DynamicUpdate๋ฌด๋ถ„๋ณ„ ์‚ฌ์šฉ
โ†’ ์˜คํžˆ๋ ค ๋‹ค๋ฅธ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์œ ๋ฐœ

After

์ƒํ™ฉ๋ณ„ ํŒ๋‹จ ๊ธฐ์ค€ํ™•๋ฆฝ
โ†’ ๋ถˆํ•„์š”ํ•œ UPDATE ์ œ๊ฑฐ + ์บ์‹œ ์œ ์ง€


JDBC ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ์™€ Statement ์บ์‹ฑ

for ๋ฌธ ์•ˆ์—์„œ save()๋ฅผ ๋ฐ˜๋ณต ํ˜ธ์ถœํ•˜๋ฉด 1๋งŒ ๊ฑด๋งŒ ๋„˜์–ด๋„ ๋”์ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ๋งˆ์ฃผํ•ฉ๋‹ˆ๋‹ค. JPA์˜ ๊ธฐ๋ณธ ๋™์ž‘๋งŒ์œผ๋กœ๋Š” ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋Š” ํ•œ๊ณ„๋ฅผ ๋›ฐ์–ด๋„˜์–ด, JDBC ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ์™€ Statement ์บ์‹ฑ์„ ์‹ค์ „์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

Before

saveAll()ํ˜ธ์ถœ ์‹œ ์—”ํ‹ฐํ‹ฐ ์ˆ˜๋งŒํผ
SELECT + INSERT ๊ฐœ๋ณ„ ์‹คํ–‰

After

์ €์žฅ ๋ฉ”์„œ๋“œ ๊ต์ฒด+ JDBC Batching
โ†’ SELECT 0๊ฑด + ๋ฐฐ์น˜ INSERT


N+1์„ ํ”ผํ•˜๋Š” Fetch ์ „๋žต

N+1 ํ•ด๊ฒฐ์„ ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ๋๋ƒˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์…จ๋‚˜์š”? ํŽ˜์น˜ ์กฐ์ธ์ด ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•˜๋Š” ์˜์—ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์„ค์ • ๊ฐ’ ํ•˜๋‚˜๋กœ ๋Œ€๋Ÿ‰ ์กฐํšŒ ์„ฑ๋Šฅ์ด 18๋ฐฐ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒฝํ—˜์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Before

๊ธฐ๋ณธ Fetch Size๋กœ 10๋งŒ ๊ฑด ์กฐํšŒ
โ†’ 550ms ์†Œ์š”

After

์„ค์ • ํ•œ ์ค„ ์ถ”๊ฐ€
โ†’ 30ms ์†Œ์š” (18๋ฐฐ ๊ฐœ์„ )


๋Œ€์šฉ๋Ÿ‰ ์กฐํšŒ๋ฅผ ์œ„ํ•œ ํŽ˜์ด์ง€๋„ค์ด์…˜๊ณผ ํ”„๋กœ์ ์…˜

"์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ–ˆ๋”๋‹ˆ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ„ฐ์ ธ๋ฒ„๋ ธ์–ด์š”." โ€” OFFSET์˜ ๊ทผ๋ณธ์  ํ•œ๊ณ„๋ฅผ ์ธ์ •ํ•˜๊ณ , ํŽ˜์ด์ง€ ์ˆ˜์— ๊ด€๊ณ„์—†์ด ์ผ์ •ํ•œ ์†๋„๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋Œ€์•ˆ์„ Spring Data ํ™˜๊ฒฝ์—์„œ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๊ผญ ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ๊ณจ๋ผ ๋‹ด๋Š” ํ”„๋กœ์ ์…˜ ๊ธฐ๋ฒ•๋„ ํ•จ๊ป˜ ๋ฐฐ์›๋‹ˆ๋‹ค.

Before

OFFSET ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜
โ†’ ๋’ค๋กœ ๊ฐˆ์ˆ˜๋ก 20๋ฐฐ ๋А๋ ค์ง

After

OFFSET์„ ๋Œ€์ฒดํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ
โ†’ ์–ด๋А ํŽ˜์ด์ง€๋“  ์ผ์ •ํ•œ ์‘๋‹ต ์†๋„


์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์˜ ์„ฑ๋Šฅ ํ•จ์ •

์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฑด ์–ด๋ ต์ง€ ์•Š์ง€๋งŒ, ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋งคํ•‘ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๋ถˆํ•„์š”ํ•œ ํ…Œ์ด๋ธ”์ด ์ƒ๊ธฐ๊ฑฐ๋‚˜ Lazy Loading์ด ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋งˆ์ฃผ์น˜๋Š” ์—ฐ๊ด€๊ด€๊ณ„ ์„ฑ๋Šฅ ํ•จ์ •๊ณผ ํ•ด๊ฒฐ ํŒจํ„ด์„ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

Before

ํŠน์ • ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์—์„œ Lazy๊ฐ€ ๋ฌด์‹œ๋˜๊ณ 
โ†’ ๋ถˆํ•„์š”ํ•œ ํ…Œ์ด๋ธ” + ์ถ”๊ฐ€ SELECT ๋ฐœ์ƒ

After

๋งคํ•‘ ํŒจํ„ด ์ „ํ™˜์œผ๋กœ
โ†’ ๋ถˆํ•„์š”ํ•œ ํ…Œ์ด๋ธ” ์ œ๊ฑฐ + Lazy ์ •์ƒ ์ž‘๋™


ํ”„๋กœ๋•์…˜ DB ๊ธฐ๋ฐ˜ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ

H2๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•ด๋„, ํ”„๋กœ๋•์…˜ DB์—์„œ๋Š” ์ „ํ˜€ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ DB ํ™˜๊ฒฝ์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ „๋žต์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

Before

H2 ์ธ๋ฉ”๋ชจ๋ฆฌ DB๋กœ ํ…Œ์ŠคํŠธ
โ†’ ํ”„๋กœ๋•์…˜์—์„œ ์„ฑ๋Šฅ ๋ฌธ์ œ ๋’ค๋Šฆ๊ฒŒ ๋ฐœ๊ฒฌ

After

ํ”„๋กœ๋•์…˜ ๋™์ผ DB์—์„œ ํ…Œ์ŠคํŠธ
โ†’ ๋ฐฐํฌ ์ „ ์„ฑ๋Šฅ ๋ฌธ์ œ ์‚ฌ์ „ ๊ฒ€์ฆ


ํŽ˜์น˜ ์กฐ์ธ๊นŒ์ง€ ํ•ด๋ดค๋‹ค๋ฉด, ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ์‹œ์ž‘์ž…๋‹ˆ๋‹ค

๊ธฐ๋ณธ๊ธฐ์— ํ•œ ๋— ์ฐจ์ด๋ฅผ ๋งŒ๋“œ๋Š” ๋””ํ…Œ์ผ์„ ๋”ํ•ด, ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ์™„์„ฑ๋„๋ฅผ ๋”ํ•˜์„ธ์š”.

์šฐ๋ฆฌ๊ฐ€ ๋ณดํ†ต ํ•˜๋Š” ์ตœ์ ํ™”

  • N+1 ์ฟผ๋ฆฌ
    ํŽ˜์น˜ ์กฐ์ธ

  • ์ปฌ๋ ‰์…˜ ํŽ˜์ด์ง•
    default_batch_fetch_size๋กœ IN ์ฟผ๋ฆฌ

  • DTO ์กฐํšŒ
    JPQL new ์ƒ์„ฑ์ž ํ‘œํ˜„์‹

  • ๋Œ€๋Ÿ‰ INSERT
    ๋‹ค๋ฃจ์ง€ ์•Š์Œ

  • save() ์˜ค๋ฒ„ํ—ค๋“œ
    ๋‹ค๋ฃจ์ง€ ์•Š์Œ

  • @DynamicUpdate
    ๋‹ค๋ฃจ์ง€ ์•Š์Œ

  • ์—ฐ๊ด€๊ด€๊ณ„ ํ•จ์ •
    ๋‹ค๋ฃจ์ง€ ์•Š์Œ

  • ํ”„๋กœ๋•์…˜ DB ํ…Œ์ŠคํŠธ
    ๋‹ค๋ฃจ์ง€ ์•Š์Œ




Vlad์˜ ๊ณ ์„ฑ๋Šฅ Spring

  • N+1 ์ฟผ๋ฆฌ
    ํŽ˜์น˜ ์กฐ์ธ์„ ๋„˜์–ด์„  ์‹ฌํ™” ์ „๋žต์œผ๋กœ 18๋ฐฐ ๊ฐœ์„ 

  • ์ปฌ๋ ‰์…˜ ํŽ˜์ด์ง•
    ํŽ˜์ด์ง€ ์ˆ˜์— ๊ด€๊ณ„์—†์ด ์ผ์ •ํ•œ ์†๋„ ์œ ์ง€

  • DTO ์กฐํšŒ
    Spring Data ์ „์šฉ ๊ฒฝ๋Ÿ‰ ์กฐํšŒ ๊ธฐ๋ฒ•

  • ๋Œ€๋Ÿ‰ INSERT
    ์„ค์ •๊ณผ ์ „๋žต ๋ณ€๊ฒฝ์œผ๋กœ ๋ฐฐ์น˜ INSERT ํ™œ์„ฑํ™”

  • save() ์˜ค๋ฒ„ํ—ค๋“œ
    ์—”ํ‹ฐํ‹ฐ ์ˆ˜๋งŒํผ ๋ฐœ์ƒํ•˜๋Š” ๋ถˆํ•„์š”ํ•œ SELECT ์™„์ „ ์ œ๊ฑฐ

  • @DynamicUpdate
    ์ผœ์•ผ ํ•  ๋•Œ vs ๊บผ์•ผ ํ•  ๋•Œ, ํŒ๋‹จ ๊ธฐ์ค€ ์ œ์‹œ

  • ์—ฐ๊ด€๊ด€๊ณ„ ํ•จ์ •
    Lazy ์‹คํŒจ, ๋ถˆํ•„์š”ํ•œ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋“ฑ ์‹ค์ „ ํ•จ์ • ํ•ด๊ฒฐ

  • ํ”„๋กœ๋•์…˜ DB ํ…Œ์ŠคํŠธ
    ํ”„๋กœ๋•์…˜ ๋™์ผ ํ™˜๊ฒฝ์—์„œ ๋ฐฐํฌ ์ „ ์‚ฌ์ „ ๊ฒ€์ฆ

์ด ๊ฐ•์˜๋ฅผ ๋งŒ๋“  ์‚ฌ๋žŒ

์ „ ์„ธ๊ณ„ ๊ฐœ๋ฐœ์ž๋“ค์ด ์‹ ๋ขฐํ•˜๋Š” ์ „๋ฌธ๊ฐ€
Vlad Mihalcea


  • JPA ์„ฑ๋Šฅ ๊ต๊ณผ์„œ, "High-Performance Java Persistence" ์ €์ž (์•„๋งˆ์กด ํ‰์  4.7)

  • 2017๋…„, Oracle ์„ ์ • 'Java Champion'

  • Java Persistence API(JPA) 2.1 ํ‘œ์ค€ ์ œ์ • ์ฐธ์—ฌ (JSR 338 Expert Group)

  • Stack Overflow Top 0.1% ๊ธฐ์—ฌ์ž

  • Hypersistence Optimizer ๊ฐœ๋ฐœ

  • ๊ธ€๋กœ๋ฒŒ ํƒ‘ํ‹ฐ์–ด IT ๊ธฐ์—… ๋‹ค์ˆ˜ ๋Œ€์ƒ JPA ์„ฑ๋Šฅ ์ปจ์„คํ„ดํŠธ


Vlad๋Š” ๋‹จ์ˆœํžˆ Hibernate๋ฅผ ์ž˜ ์•„๋Š” ์ „๋ฌธ๊ฐ€๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
Spring Data JPA์˜ ์„ฑ๋Šฅ ํ•œ๊ณ„๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ง์ ‘ ๋Œ€์•ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“  ์‚ฌ๋žŒ์ž…๋‹ˆ๋‹ค.
์ด ๊ฐ•์˜์—์„œ๋Š” ๊ทธ๊ฐ€ ์™œ ์ด ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”์ง€, ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€๋ฅผ ์ง์ ‘ ๋ฐฐ์›๋‹ˆ๋‹ค.


์ „ ์„ธ๊ณ„ ์ž๋ฐ” ์ฑ”ํ”ผ์–ธ๋“ค์˜ ์ถ”์ฒœ์‚ฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”!


Rafael Winterhalter
(Byte Buddy ์ฐฝ์‹œ์ž
Java ์ฑ”ํ”ผ์–ธ)

Lukas Eder
(JOOQ ์ฐฝ๋ฆฝ์ž
Java ์ฑ”ํ”ผ์–ธ)

Markus Eisele
(Red Hat ๊ธฐ์ˆ  ์ „๋„์‚ฌ
Java ์ฑ”ํ”ผ์–ธ)

JDBC๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋“  JPA๋‚˜ Hibernate๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉํ•˜๋“ , ๊ทธ ์„ฑ๋Šฅ ์˜ํ–ฅ์„ ์ด๋ณด๋‹ค ๋” ์ž˜ ์ •๋ฆฌํ•œ ์ž๋ฃŒ๋Š” ์—†์Šต๋‹ˆ๋‹ค.

์ด ์ฑ…์€ JAVA ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์„ฑ๋Šฅ์„ ๊ทนํ•œ๊นŒ์ง€ ๋Œ์–ด์˜ฌ๋ฆฌ๊ณ ์ž ํ•˜๋Š” ๋ชจ๋“  ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ•„๋…์„œ์ž…๋‹ˆ๋‹ค.

Vlad๋Š” ์ž์‹ ์˜ ์—„์ฒญ๋‚œ ๊ฒฝํ—˜์„ ์ฝ๊ธฐ ์‰ฌ์šด ์ฑ…์œผ๋กœ ๋‹ด์•„๋ƒˆ๊ณ , JPA๋‚˜ Hibernate๋ฅผ ๋‹ค๋ฃฌ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ์ฝ์–ด์•ผ ํ•  ์ฑ…์ž…๋‹ˆ๋‹ค.



์ˆ˜๊ฐ• ์ „ ์ฐธ๊ณ  ์‚ฌํ•ญ

ํ˜„์žฌ ๋งˆ์ง€๋ง‰ ์„น์…˜์ธ 'Fetching ์ตœ์ ํ™”' ํŒŒํŠธ๊ฐ€ ์—ฐ์žฌ(์—…๋ฐ์ดํŠธ) ์ค‘์— ์žˆ์Šต๋‹ˆ๋‹ค.
์•ž์œผ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฃผ์ œ๋“ค์ด ์ˆœ์ฐจ์ ์œผ๋กœ ๋‹ค๋ค„์งˆ ์˜ˆ์ •์ด๋‹ˆ, ํ•™์Šต์— ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

<์—…๋ฐ์ดํŠธ ์˜ˆ์ • ๋ฆฌ์ŠคํŠธ>

  • ์—”ํ‹ฐํ‹ฐ ํŽ˜์นญ(entity fetching)

  • ์—”ํ‹ฐํ‹ฐ ์—ฐ๊ด€๊ด€๊ณ„ (FetchType.EAGER, FetchType.LAZY, LazyInitializationException, JOIN FETCH, Blaze Persistence MULTISET)

  • ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ(Stream processing)

  • Query Plan Cache

์‹ค์Šต ํ™˜๊ฒฝ

  • ์šด์˜์ฒด์ œ: ์œˆ๋„์šฐ, macOS, ๋ฆฌ๋ˆ…์Šค ๋ชจ๋‘ ๊ฐ€๋Šฅ

  • ํ•„์ˆ˜ ๋„๊ตฌ: JDK 17 ์ด์ƒ, IntelliJ IDEA ๋˜๋Š” Eclipse IDE

  • ๋นŒ๋“œ ๋„๊ตฌ: Maven, Gradle

  • ๊ถŒ์žฅ ์‚ฌ์–‘: 8GB ์ด์ƒ์˜ RAM๊ณผ ์ถฉ๋ถ„ํ•œ ๋””์Šคํฌ ๊ณต๊ฐ„

์„ ์ˆ˜ ์ง€์‹ ๋ฐ ์œ ์˜์‚ฌํ•ญ

  • Java ๋ฐ Spring ํ”„๋ ˆ์ž„์›Œํฌ ๊ธฐ๋ณธ ์ง€์‹

  • Spring Data JPA ์‚ฌ์šฉ ๊ฒฝํ—˜ (JpaRepository, @Entity)

  • ๊ธฐ๋ณธ์ ์ธ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(RDB) ๋ฐ SQL ์ง€์‹

  • ์‹ค๋ฌด์—์„œ Spring ๊ธฐ๋ฐ˜ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ๋”์šฑ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

ํ•™์Šต ์ž๋ฃŒ

  • ๊ฐ•์˜ ์Šฌ๋ผ์ด๋“œ PDF ํŒŒ์ผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ์‹ค์Šต ์ฝ”๋“œ๋Š” GitHub ์ €์žฅ์†Œ๋ฅผ ํ†ตํ•ด ์ œ๊ณต๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

  • ๊ฐ ์„น์…˜ ๋งˆ์ง€๋ง‰์— ๊ฐ•์˜ ๋‚ด์šฉ์„ ๋ณต์Šตํ•  ์ˆ˜ ์žˆ๋Š” ํ€ด์ฆˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ”— ๊ธฐ์กด JPA ๊ฐ•์˜์™€์˜ ์—ฐ๊ด€์„ฑ

  • ๋‹ค๋ฅธ JPA ์„ฑ๋Šฅ ๊ฐ•์˜ ์ˆ˜๊ฐ• ์—ฌ๋ถ€๋Š” ํ•„์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

  • ์ˆ˜๊ฐ•ํ•˜์…จ๋‹ค๋ฉด ์‹ฌํ™” ๋‚ด์šฉ์„ ๋” ๊นŠ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์ฒด๊ฐ€ ์•„๋‹Œ ๋ณด์™„ ๊ด€๊ณ„์˜ ๊ฐ•์˜์ž…๋‹ˆ๋‹ค.


  • ํŽ˜์น˜ ์กฐ์ธ, batch_fetch_size ๊ฐ™์€ ๊ธฐ๋ณธ ์ตœ์ ํ™”๋ฅผ ๋„˜์–ด์„  ์‹ฌํ™” ๊ณผ์ •์ž…๋‹ˆ๋‹ค


์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ

Q. ํŽ˜์น˜ ์กฐ์ธ์ด๋‚˜ batch_fetch_size ์ •๋„๋Š” ์•„๋Š”๋ฐ, ์ด ๊ฐ•์˜๊ฐ€ ํ•„์š”ํ•œ๊ฐ€์š”?

๋„ค, ๋ฐ”๋กœ ๊ทธ ์ง€์ ์—์„œ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ•์˜์ž…๋‹ˆ๋‹ค. ์ด ๊ฐ•์˜๋Š” ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„์ธ ์“ฐ๊ธฐ(Write)์™€ DB ๋ ˆ์ด์–ด ์ตœ์ ํ™”๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

Q. ๊ธฐ์กด ใ€Œ๊ณ ์„ฑ๋Šฅ JPA & Hibernateใ€ ๊ฐ•์˜์™€ ๋ญ๊ฐ€ ๋‹ค๋ฅธ๊ฐ€์š”?

๊ธฐ์กด ๊ฐ•์˜๋Š” EntityManager/Session API๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋ฉฐ Hibernate ์—”์ง„์˜ ๋‚ด๋ถ€ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
์ด ๊ฐ•์˜๋Š” JpaRepository์™€ @Transactional์„ ์‚ฌ์šฉํ•˜๋Š” Spring Data JPA ํ™˜๊ฒฝ์—์„œ ์‹ค์ œ๋กœ ๋งˆ์ฃผ์น˜๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
๊ฐ™์€ "Batching"์ด๋ผ๋Š” ์ฃผ์ œ๋ผ๋„, ๊ธฐ์กด ๊ฐ•์˜๋Š” "JDBC Batch๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€"๋ฅผ, ์ด ๊ฐ•์˜๋Š” "์™œ Spring Data์—์„œ saveAll()์„ ํ•ด๋„ ๋ฐฐ์น˜๊ฐ€ ์•ˆ ๋˜๋Š”๊ฐ€"๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

Q. JPA/Hibernate๋ฅผ ์–ด๋А ์ •๋„ ์•Œ์•„์•ผ ํ•˜๋‚˜์š”?

Spring Data JPA๋กœ CRUD๋ฅผ ๊ตฌํ˜„ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์œผ๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
JpaRepository, @Entity, @Transactional์„ ์‚ฌ์šฉํ•ด๋ณธ ์ˆ˜์ค€์ด๋ฉด ๋ชจ๋“  ๋‚ด์šฉ์„ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ๊ฐ•์˜(๊ณ ์„ฑ๋Šฅ JPA & Hibernate)๋ฅผ ๋“ค์—ˆ๋‹ค๋ฉด ๋” ๊นŠ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•„์ˆ˜๋Š” ์•„๋‹™๋‹ˆ๋‹ค.

Q. ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋‚˜์š”?

MySQL, PostgreSQL, Oracle, SQL Server 4๊ฐœ DB์—์„œ์˜ ๋น„๊ต ๋ฒค์น˜๋งˆํฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
H2๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ํ”„๋กœ๋•์…˜ DB ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ถ„๋“ค๊ป˜
์ถ”์ฒœ๋“œ๋ ค์š”

ํ•™์Šต ๋Œ€์ƒ์€
๋ˆ„๊ตฌ์ผ๊นŒ์š”?

  • ๊ธฐ์กด JPA ์„ฑ๋Šฅ ๊ฐ•์˜๋ฅผ ์ˆ˜๊ฐ•ํ•œ ๋’ค, ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ ๋ถ„

  • ํŽ˜์น˜ ์กฐ์ธ๊ณผ batch_fetch_size ๊นŒ์ง€๋Š” ์ ์šฉํ–ˆ๋Š”๋ฐ, ๊ทธ ๋‹ค์Œ์— ๋ญ˜ ํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๊ฒ ๋Š” ๋ถ„

  • save() ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์˜ˆ์ƒ ๋ชป ํ•œ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ€๋Š” ๊ฑธ ๊ฒฝํ—˜ํ–ˆ์ง€๋งŒ ์›์ธ์„ ๋ชป ์ฐพ์€ ๋ถ„

  • JPA ์„ฑ๋Šฅ ํŠœ๋‹์„ ํ•ด๋ดค์ง€๋งŒ, DB ๋ ˆ์ด์–ด๊นŒ์ง€ ๋‚ด๋ ค๊ฐ€๋ณธ ์ ์€ ์—†๋Š” ๋ถ„

  • ์‹ค๋ฌด์—์„œ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋ฉด์„œ ์กฐํšŒ๋ฟ ์•„๋‹ˆ๋ผ ์“ฐ๊ธฐ ์„ฑ๋Šฅ ๊นŒ์ง€ ์žก์•„์•ผ ํ•˜๋Š” ๋ถ„

์„ ์ˆ˜ ์ง€์‹,
ํ•„์š”ํ• ๊นŒ์š”?

  • Spring Data JPA ์‚ฌ์šฉ ๊ฒฝํ—˜

  • ๊ธฐ๋ณธ์ ์ธ Spring Framework / Spring Boot ์ง€์‹

  • ๊ธฐ๋ณธ์ ์ธ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(RDB) ๋ฐ SQL ์ง€์‹

์•ˆ๋…•ํ•˜์„ธ์š”
Vlad Mihalcea์ž…๋‹ˆ๋‹ค.

1,289

๋ช…

์ˆ˜๊ฐ•์ƒ

74

๊ฐœ

์ˆ˜๊ฐ•ํ‰

4.5

์ 

๊ฐ•์˜ ํ‰์ 

3

๊ฐœ

๊ฐ•์˜

My name is Vlad Mihalcea, and Iโ€™m a Java Champion. I wrote the High-Performance Java Persistence book, which became one of the best-selling Java books on Amazon.

 

I'm currently developing the amazing Hypersistence Optimizer, and in my free time, I develop various open-source projects (e.g., Hypersistence Utils and FlexyPool) and answer questions on StackOverflow.

 

์ €๋Š” Java ์ฑ”ํ”ผ์–ธ์ด์ž Hibernate ORM ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” ๊ธฐ์—ฌ์ž์ž…๋‹ˆ๋‹ค. Hypersistence Optimizer ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ, ์ด ๋„๊ตฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑ๊ณผ ๋งคํ•‘์„ ์Šค์บ”ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ์— ํ•„์š”ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์•Œ๋ ค์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

StackOverflow์—์„œ Hibernate, Java, JPA ํƒœ๊ทธ์— ๊ด€๋ จ๋œ ์ˆ˜์ฒœ ๊ฐœ์˜ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•˜๋ฉฐ ๊ณจ๋“œ ๋ฐฐ์ง€๋ฅผ ํš๋“ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํฅ๋ฏธ๋กœ์šด ๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ๊ฐœ์ธ ๋ธ”๋กœ๊ทธ์— ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Š” ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‹ ๋ขฐํ•˜๋ฉฐ, ๋ชจ๋“  ๊ฐœ๋ฐœ์ž๊ฐ€ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋“  ์ฐธ์—ฌํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ ์ ˆํ•œ ๋„๊ตฌ๋ฅผ ์ฐพ์ง€ ๋ชปํ•˜๋ฉด Hypersistence Utils๋‚˜ FlexyPool๊ณผ ๊ฐ™์€ ์ƒˆ๋กœ์šด ์˜คํ”ˆ ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

๋”๋ณด๊ธฐ

์ปค๋ฆฌํ˜๋Ÿผ

์ „์ฒด

33๊ฐœ โˆ™ (5์‹œ๊ฐ„ 17๋ถ„)

ํ•ด๋‹น ๊ฐ•์˜์—์„œ ์ œ๊ณต:

์ˆ˜์—…์ž๋ฃŒ
๊ฐ•์˜ ๊ฒŒ์‹œ์ผ: 
๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ์ผ: 

์ˆ˜๊ฐ•ํ‰

์•„์ง ์ถฉ๋ถ„ํ•œ ํ‰๊ฐ€๋ฅผ ๋ฐ›์ง€ ๋ชปํ•œ ๊ฐ•์˜์ž…๋‹ˆ๋‹ค.
๋ชจ๋‘์—๊ฒŒ ๋„์›€์ด ๋˜๋Š” ์ˆ˜๊ฐ•ํ‰์˜ ์ฃผ์ธ๊ณต์ด ๋˜์–ด์ฃผ์„ธ์š”!

Vlad Mihalcea๋‹˜์˜ ๋‹ค๋ฅธ ๊ฐ•์˜

์ง€์‹๊ณต์œ ์ž๋‹˜์˜ ๋‹ค๋ฅธ ๊ฐ•์˜๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”!

๋น„์Šทํ•œ ๊ฐ•์˜

๊ฐ™์€ ๋ถ„์•ผ์˜ ๋‹ค๋ฅธ ๊ฐ•์˜๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”!

์–ผ๋ฆฌ๋ฒ„๋“œ ํ• ์ธ ์ค‘

์›” โ‚ฉ28,050

5๊ฐœ์›” ํ• ๋ถ€ ์‹œ

50%

โ‚ฉ280,500

โ‚ฉ140,250