๐Ÿšฆ Spring Batch ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

์ฒซ ๋ฒˆ์งธ ์ด์Šˆ

์ฒญํฌ์‚ฌ์ด์ฆˆ๊ฐ€ ๋‹ค๋ฆ„์—๋„ ์ฒ˜๋ฆฌ์†๋„๊ฐ€ ๋˜‘๊ฐ™์€ ์ด์œ ๊ฐ€ ๋ญ˜๊นŒ?

** Chunk ๋ฐฉ์‹์˜ Batch์—์„œ ChunkSize๋ž€, ํ•œ ํŠธ๋ Œ์ ์…˜ ๋‚ด์—์„œ ์ฒ˜๋ฆฌํ•  ์ปฌ๋Ÿผ(DTO/๋ชจ๋ธ)์˜ ๊ฐœ์ˆ˜์ด๋‹ค.

์ฆ‰, ChunkSize๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋ฐ์ดํ„ฐ I/O์ž‘์—… ๋ฐ Overhead(๋ฐ์ดํ„ฐ ์ฝ๊ธฐ/์“ฐ๊ธฐ, ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘ ๋ฐ ์ข…๋ฃŒ ๋“ฑ)๊ฐ€ ์ฆ๊ฐ€ํ•˜์—ฌ ์ด ์‹คํ–‰์‹œ๊ฐ„์ด ๊ธธ์–ด์ ธ์•ผํ•œ๋‹ค.

  • ์ฒญํฌ ์‚ฌ์ด์ฆˆ๋ณ„ ์‹คํ–‰์‹œ๊ฐ„ ์ธก์ • ๋ฐ์ดํ„ฐ
grid-size:12 / chunk-size:30
-
3๋ถ„ 22.856์ดˆ
3๋ถ„ 23.096์ดˆ

grid-size:12 / chunk-size:20
-
3๋ถ„ 23.546์ดˆ
3๋ถ„ 23.784์ดˆ


grid-size:12 / chunk-size:10
-
3๋ถ„ 24.243์ดˆ
3๋ถ„ 22.389์ดˆ
3๋ถ„ 24.667์ดˆ
3๋ถ„ 24.789์ดˆ

grid-size:12 / chunk-size:5
-
3๋ถ„ 24.953์ดˆ
3๋ถ„ 24.353์ดˆ

โ–ถ ์œ ์˜๋ฏธํ•œ ์ฐจ์ด๋ฅผ ๋ณด๊ธฐ์–ด๋ ต๋‹ค.


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

์˜ˆ์ƒ - ํŠธ๋ Œ์ ์…˜์ด ๋ถ„๋ฆฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋˜์ง€ ์•Š์•˜๋‚˜?

์›์ธ์ด ๋ฌด์—‡์ผ๊นŒ?

  • ๋ฐฐ์น˜ ์„œ๋น„์Šค ๋กœ์ง์˜ ์ผ๋ถ€
dtoList.stream().parallel()  
        .forEach(dto -> {});
  • ์Šค๋ ˆ๋“œ ํ™•์ธ
}
	}
        IntStream.range(1, 10)
                .parallel()
                .forEach(i -> System.out.println(Thread.currentThread().getName() + " - " + i));
    }
}

- ์ถœ๋ ฅ
ForkJoinPool.commonPool-worker-3 - 3
ForkJoinPool.commonPool-worker-1 - 1
ForkJoinPool.commonPool-worker-2 - 2
ForkJoinPool.commonPool-worker-0 - 4
...


ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋ ๊นŒ?

  • ๋ณ„๋„์˜ ์…‹ํŒ…์ด ์—†์ด parallel() ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค๋ฉด, 1.๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์€ ๋‹จ์œ„(Chunk)๋กœ ๋ถ„ํ•  - ์˜ˆ๋ฅผ ๋“ค์–ด, 1000๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ 4๊ฐœ์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ํ•˜๋ฉด, ForkJoinPool์€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ์˜ Task๋กœ ๋‚˜๋ˆˆ๋‹ค. 2.Worker Thread๋“ค์ด ๋ถ„ํ• ๋œ ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ - ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ์ด ๋งก์€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋‚จ๋Š” ์ž‘์—…์ด ์žˆ๋‹ค๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์ž‘์—…์„ ํ›”์ณ(Work-Stealing) ๊ฐ€์ ธ์™€ ์‹คํ–‰ํ•œ๋‹ค. 3.์ตœ์ข…์ ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์ณ์„œ ๋ฐ˜ํ™˜ - ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์ง„๋‹ค
ForkJoinPool์ด๋ž€?

Java์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ณ‘๋ ฌ ์ž‘์—…์„ ์ตœ์ ํ™”ํ•˜๋Š” ์Šค๋ ˆ๋“œ ํ’€๋กœ์จ,
Work-Stealing ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์œ ํœด์Šค๋ ˆ๋“œ๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ  CPU ํ™œ์šฉ๋„๋ฅผ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค.

ํ•ด๋‹น ๊ณผ์ •์—์„œ ForkJoinPool์€ ์ž‘์—…์„ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์›Œ์ปค ์Šค๋ ˆ๋“œ(ForkJoinPool-worker-*)์—์„œ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Spring์˜ ThreadLocal ๊ธฐ๋ฐ˜ ํŠธ๋žœ์žญ์…˜์ด ์ „ํŒŒ๋˜์ง€ ์•Š๋Š”๋‹ค.


๊ฒฐ๋ก 

  • ์ฒญํฌ์˜ ๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ์ง์—์„œ์˜ ๋™์ž‘ ํšจ์œจ์„ ์œ„ํ•ด ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜, ForkJoinPool๊ธฐ๋ฐ˜์˜ ์ •ํ™•ํ•œ ๋™์ž‘์›๋ฆฌ๋ฅผ ์ถฉ๋ถ„ํžˆ ๊ณ ๋ คํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•œ ์ด์Šˆ์ด๋‹ค.
  • ํ•ด๋‹น ๋ถ€๋ถ„์€ ์ผ๋ฐ˜์ ์ธ forEach๋ฌธ์œผ๋กœ ๋ณ€๊ฒฝํ•จ์œผ๋กœ์จ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํŠนํžˆ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ์—๋Š” ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ํ•จ์— ์žˆ์–ด ์ฃผ์˜๋ฅผ ํ•„์š”๋กœ ํ•จ์„ ๊นจ๋‹ฌ์•˜๋‹ค.



๋‘ ๋ฒˆ์งธ ์ด์Šˆ

๋ฐ˜๋ณต TEST ์ค‘.. ์ปค๋„ฅ์…˜ํ’€ Time Out ๋ฌธ์ œ??

Pasted image 20241213102128.png

DB ์ปค๋„ฅ์…˜ ํ’€ ๊ฐœ์ˆ˜ ํ™•์ธ

SHOW VARIABLES LIKE 'max_connections'; //์ตœ๋Œ€ ๊ฐœ์ˆ˜
SHOW STATUS LIKE 'Threads_connected'; //์‚ฌ์šฉ์ค‘์ธ ๊ฐœ์ˆ˜

Pasted image 20250304170347.png Pasted image 20250304170412.png Pasted image 20250304171240.png

  • ์šด์˜ DB์˜ ์ปค๋„ฅ์…˜ pool์€ ์ถฉ๋ถ„ํ•œ ์ƒํƒœ๋กœ ๋ณด์ธ๋‹ค.

์ •๋ณด

์ฒ˜์Œ ๋ช‡ ๋ฒˆ๊ฐ„์€ ์ •์ƒ์‹คํ–‰ ๋˜์ง€๋งŒ, ๋ฐ˜๋ณต ํ…Œ์ŠคํŠธ ์ค‘ ์Šค๋ ˆ๋“œ ํ’€ ์ ์œ  ๋Œ€๊ธฐ ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ–ˆ๋‹ค.

  • ๋ฐฐ์น˜๋ฅผ ์™„๋ฃŒํ•œ ์ดํ›„์—, ์Šค๋ ˆ๋“œ ํ’€(ํžˆ์นด๋ฆฌ ํ’€)์„ ์ •์ƒ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธ.

๋ฐฐ์น˜๋ฅผ ๋ฐ˜๋ณตํ•  ์ˆ˜๋ก ์ปค๋„ฅ์…˜ ์—‘ํ‹ฐ๋ธŒ๋œ ์ปค๋„ฅ์…˜ ํ’€ ๊ฐœ์ˆ˜๊ฐ€ ์ ์  ๋Š˜์–ด๋‚˜๋Š” ๋ชจ์Šต

Pasted image 20241213102229.png


์›์ธ?

๋จผ์ €, QuerydslPagingItemReader์˜ doReadPage()์˜ ์ข…๋ฃŒ์กฐ๊ฑด์—์„œ ํŠธ๋ Œ์ ์…˜ ์ปค๋ฐ‹์„ ๋ณ„๋„๋กœ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋ฆฌํ„ด์„ ์‹œํ‚ค๊ณ  ์žˆ์—ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์ด ์ข…๋ฃŒ(connection clos)๊ฐ€ ์ผ์–ด๋‚˜๋ฉด ํŠธ๋ Œ์ ์…˜๋„ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š”๊ฐ€??

-> ๊ทธ๋Ÿผ์—๋„ Step์ด ๋งˆ๋ฌด๋ฆฌ๋ ๋•Œ, ์ ์–ด๋„ Job์ด ๋งˆ๋ฌด๋ฆฌ ๋ ๋•Œ, entityManager๋ฅผ ํด๋กœ์ฆˆ ์‹œํ‚ค๋Š”๊ฒƒ์ด ์ž๋ช…ํ•œ๋ฐ, ์–ด์งธ์„œ ์ปค๋„ฅ์…˜ํ’€์ด ํ•ด์ œ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€.

  • ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ ํด๋กœ์ฆˆ ๋˜์—ˆ์Œ์—๋„ ์ปค๋„ฅ์…˜ํ’€์„ ๋ฌผ๊ณ ์žˆ๋Š” ๋ชจ์Šต. Pasted image 20241213143101.png
  • ์ฝ”๋“œ๋ฅผ ๋œฏ์–ด๋ณด์ž...

๋จผ์ €, ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ์ปค๋„ฅ์…˜์˜ ๋ฐ˜ํ™˜์„ ์œ„์ž„๋ฐ›๋Š”๋‹ค. Pasted image 20250314120439.png ๊ทธ๋ฆฌ๊ณ .. transactionObserver๋Š” ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ๊ฐ์ฒด์™€ ์—ฐ๊ฒฐ๋œ ํŠธ๋žœ์ ์…˜ ๋งค๋‹ˆ์ €๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ์—ˆ๋‹ค.

์ฆ‰,

์ฃผ์˜

์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ํด๋กœ์ฆˆ ๋  ๋•Œ, ํŠธ๋ Œ์ ์…˜์ด ์‚ด์•„์žˆ๋‹ค๋ฉด ๊ทธ ํŠธ๋ Œ์ ์…˜์ด ์ข…๋ฃŒ๋ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.

Pasted image 20241213143217.png


๊ฒฐ๋ก 

  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ ํด๋กœ์ฆˆ ๋  ๋–„, ์ปค๋„ฅ์…˜ pool์ด ๋ฐ˜ํ™˜๋ ๊ฑฐ๋ผ๋Š” ๊ธฐ๋Œ€์™€๋Š” ๋‹ฌ๋ฆฌ, ํŠธ๋ Œ์ ์…˜์ด ์ปค๋ฐ‹ ๋  ๋•Œ ๊นŒ์ง€ ์ปค๋„ฅ์…˜pool์„ ๊ณ„์†ํ•ด์„œ ๋ฌผ๊ณ  ์žˆ์—ˆ๋‹ค. (reader๋Š” ํŽ˜์ด์ง•์„ ์œ„ํ•œ ๋ณ„๋„์˜ ํŠธ๋ Œ์ ์…˜ ์ƒ์„ฑ)

๋‹จ์ˆœ ์กฐํšŒ(select)์ฟผ๋ฆฌ๋Š” ํŠธ๋ Œ์ ์…˜์„ ์ค‘์š”ํ•˜๊ฒŒ ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ์„ค๊ณ„๋ฅผ ํ•ด์™”์ง€๋งŒ, ์ด๋ฒˆ ์ปค๋„ฅ์…˜Pool ์ ์œ  ์ด์Šˆ์—์„œ ํŠธ๋ Œ์ ์…˜ ์„ค๊ณ„์˜ ์ค‘์š”์„ฑ์„ ๋‹ค์‹œ๊ธˆ ๊นจ๋‹ฌ์•˜๋‹ค.


์ˆ˜์ • ํ›„

protected void doReadPage() {  
	...
        if (startIndex >= totalRecords) {  //๋งˆ์ง€๋ง‰์ธ๋ฑ์Šค ํ™•์ธ
            initResults(); // ๋นˆ ๊ฒฐ๊ณผ๋กœ ์ดˆ๊ธฐํ™”  
            tx.commit();  //ํŠธ๋ Œ์ ์…˜ ์ปค๋ฐ‹
            return;  
        }  
	...

์ •์ƒ์ ์œผ๋กœ ์ปค๋„ฅ์…˜ ํ’€์ด ์ ์œ  ํ•ด์ œ๋œ ๋ชจ์Šต

Pasted image 20241213103503.png