JiwonDev

์Šคํ”„๋ง ๋ฐฐ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฒช์€ ๊ฒƒ๋“ค

by JiwonDev

๋จผ์ € ์Šคํ”„๋ง ๋ฐฐ์น˜๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

 

 

๐Ÿ€ ์Šคํ”„๋ง ๋ฐฐ์น˜๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ์š”?

์Šคํ”„๋ง ๋ฐฐ์น˜๋Š” Job ๋‹จ์œ„๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ Job์€ ์—ฌ๋Ÿฌ Step์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

 

์‹คํ–‰ํ•˜๋ฉด์„œ Spring Batch ๋‚ด๋ถ€์˜ Job Repository๋ฅผ ํ†ตํ•ด ๊ณ„์†ํ•ด์„œ Job๊ณผ Step์˜ ์ •๋ณด๋ฅผ ์•„๋ž˜์˜ ์ˆœ์„œ๋Œ€๋กœ ๋ฉ”ํƒ€ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ ๋ฉ”ํƒ€ ์ •๋ณด๋Š” ๋ฐฐ์น˜ ์ˆ˜ํ–‰๊ณผ ๊ด€๋ จ๋œ ์ˆ˜์น˜(์‹œ์ž‘/์ข…๋ฃŒ ์‹œ๊ฐ„, ์ƒํƒœ, ํšŸ์ˆ˜)์™€ Job, Step์—์„œ ๊ณต์œ ํ•ด์„œ ์“ฐ๋Š” ์ปจํ…์ŠคํŠธ๋“ฑ์„ ํฌํ•จํ•œ๋‹ค.

  • BATCH_JOB_INSTANCE :  JOB ์ธ์Šคํ„ด์Šค ์ •๋ณด - Job ์ด๋ฆ„, Job ๊ณ ์œ  ํ‚ค
  • BATCH_JOB_EXECUTION :  JOB ์‹คํ–‰์ •๋ณด - job_instance_id, ์ƒํƒœ, ์‹œ์ž‘์‹œ๊ฐ„, ์ข…๋ฃŒ์‹œ๊ฐ„, job ์—๋Ÿฌ์ •๋ณด 
  • BATCH_JOB_EXECUTION_PARAM :  ์‚ฌ์šฉ๋œ Job Parameter ์ •๋ณด
  • BATCH_JOB_EXECUTION_CONTEXT :  ์‚ฌ์šฉ๋œ Job Execution Context ๊ฐ์ฒด  (์ง๋ ฌํ™”๋˜์–ด DB์—๋„ ์ €์žฅ) 
  • BATCH_STEP_EXECUTION :  STEP ์‹คํ–‰์ •๋ณด - job_execution_id, ๋ฐ์ดํ„ฐ read, commit, filter, skip count, step ์—๋Ÿฌ์ •๋ณด
  • BATCH_STEP_EXECUTIOIN_CONTEXT : ์‚ฌ์šฉ๋œ Step Execution Context ๊ฐ์ฒด (์ง๋ ฌํ™”๋˜์–ด DB์—๋„ ์ €์žฅ)

 

 

 

 

๐Ÿ€ ์Šคํ”„๋ง ๋ฐฐ์น˜์˜ ํŠธ๋žœ์žญ์…˜์€ ์–ด๋–ป๊ฒŒ ๊ฑธ๋ฆด๊นŒ์š”?

 

์œ„ ๊ทธ๋ฆผ์—์„œ ํŠธ๋žœ์žญ์…˜ ๋ถ€๋ถ„์„ ํ™•๋Œ€ํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ์ฐธ๊ณ ๋กœ Reader์—์„œ DB๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ spring batch ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜๋ฉด์„œ ๋ฐ”๋กœ DB ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋จ์„ ์œ ์˜ํ•˜์ž. 

Chunk ๋‹จ์œ„๋กœ ํŠธ๋žœ์žญ์…˜์ด ๋ฐ˜๋ณต๋œ๋‹ค.

์ฐธ๊ณ ๋กœ ์ด๋Š” ChunkOrientedTasklet ๋ฅผ ๋””๋ฒ„๊น…์œผ๋กœ ์ถ”์ ํ•ด๋ณด๋ฉด ์ฝ”๋“œ๋กœ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ •ํ™•ํžˆ๋Š” TaskletStep ์—์„œ ์ž‘์—…ํ•  ๋•Œ ๋งˆ๋‹ค StepExecution์ด ์—…๋ฐ์ดํŠธ ๋˜๊ณ  ์ด๋Š” ์ฒญํฌ ๋‹จ์œ„๋กœ JobRepository์— ์˜ํ•ด DB์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

 

 

 

๐Ÿค” ์Šคํ”„๋ง ๋ฐฐ์น˜๋Š” ์™œ ์ด๋ ‡๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ๊ธธ๊ฒŒ ์žก์„๊นŒ์š”?

๋ฌผ๋ก  ๋‹จ์ˆœํžˆ ํŠธ๋žœ์žญ์…˜์„ ์ฒญํฌ๋‹จ์œ„๋กœ ๊ฑธ๋ฉด ๊น”๋”ํ•˜๋‹ˆ๊นŒ ๊ทธ๋Ÿฐ ๊ฒƒ๋„ ์žˆ๊ฒ ์ง€๋งŒ

  • SpringBatch๋Š” ์ฒญํฌ๊ฐ€ ์‹คํ–‰ ๋  ๋•Œ ๋งˆ๋‹ค StepExecution ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ, ์ฒญํฌ๊ฐ€ ๋๋‚˜๋Š” ์‹œ์ ์— DB์— ์ €์žฅํ•œ๋‹ค. ์ด๋Š” ๋ฐฐ์น˜ ์ž‘์—…์˜ ์ถ”์ ๊ณผ ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์žฌ์‹œ์ž‘์„ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
  • ๋ณต์žกํ•œ ๋ฐฐ์น˜ ์ž‘์—…์— TransactionCallback์„ ์‚ฌ์šฉ ์‹œ ๋ฐ๋“œ๋ฝ์ด ๊ฑธ๋ฆฌ๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์–ด์„œ ์ฒญํฌ๋‹จ์œ„๋กœ ํŠธ๋žœ์žญ์…˜์„ ์žก์€๊ฑฐ๊ธฐ๋„ ํ•˜๋‹ค.

 

 

 

1๏ธโƒฃ TransactionSystemException (Transaction Session Timeout)

์‚ฌ๋‚ด์—์„œ ๋ฐฐ์น˜๋ฅผ ์ž˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ๋„ ํŠน์ • ์‹œ์  ์ดํ›„๋ถ€ํ„ฐ ์•„๋ž˜ ์—๋Ÿฌ๊ฐ€ ์ข…์ข… ๋ฐœ์ƒํ•˜์˜€์—ˆ๋‹ค.

org.springframework.transaction.TransactionSystemException: Could not roll back JPA transaction; nested exception is org.hibernate.TransactionException: Unable to rollback against JDBC Connection
	at org.springframework.orm.jpa.JpaTransactionManager.doRollback(JpaTransactionManager.java:593)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:835)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:809)
	at org.springframework.transaction.support.TransactionTemplate.rollbackOnException(TransactionTemplate.java:168)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:144)
	...

 

๋ฐ์ดํ„ฐ๋ฅผ API๋กœ ๋ฐ›์•„์™€์„œ ์ „์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฐ์น˜์˜€๋Š”๋ฐ Reader์™€ Processor์—์„œ๋Š” DB ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ์˜€๋‹ค.

Writer ๋˜ํ•œ ๋‹จ์ˆœ ์ „์ฒ˜๋ฆฌ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ Saveํ•˜๋Š” ๋‹จ์ˆœํ•œ ์ฟผ๋ฆฌ๋ผ ๋”๋”์šฑ ์ดํ•ด๊ฐ€ ์•ˆ๋˜๋Š” ์ƒํ™ฉ์ด์—ˆ๋‹ค.

/** ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. **/
@Bean
fun JiwonCollectDataJob(
    jiwonStep: Step,
): Job {
    return jobBuilderFactory.get(JIWON_JOB_NAME)
        .start(jiwonStep)
        .incrementer(RunIdIncrementer())
        .listener(JobResultListener())
        .build()
}

@Bean
fun JiwonStep(
    jiwonItemReader: JiwonItemReader,
    jiwonItemProcessor: ItemProcessor<List<CollectData>, List<MyEntity>>,
    jiwonItemWriter: ItemWriter<List<MyEntity>>,
): Step {
    // Reader(์›น์‚ฌ์ดํŠธ API) - Processor(๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ) - Writer(๋‹จ์ˆœ DB ์ €์žฅ)
    return stepBuilderFactory[JIWON_STEP_NAME]
        .chunk<List<ParseData>, List<Entity>>(chunkSize)
        .reader(jiwonItemReader)
        .processor(jiwonItemProcessor)
        .writer(jiwonItemWriter)
        .transactionManager(mainDbTransactionManager)
        .build()
}

 

 

 

๐Ÿค” ์™œ Transaction Timeout์ด ๋ฐœ์ƒํ–ˆ๋Š”๊ฐ€?

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด Hikari Connection ๊ฐ์ฒด๋Š” ์‚ด์•„์žˆ๋Š”๋ฐ db session timeout ์„ ์ดˆ๊ณผํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

// ๊ด€๋ จ๋œ ๋กœ๊ทธ (Spring, db, HikariCP)
org.springframework.transaction.TransactionSystemException: Could not roll back JPA transaction; nested exception is org.hibernate.TransactionException: Unable to rollback against JDBC Connection
FATAL: terminating connection due to idle-in-transaction timeout
HikariCP-Writer - Connection org.postgresql.jdbc.PgConnection@2d0ea3a9 marked as broken because of SQLSTATE(08006), ErrorCode(0) // 08006์€ ์ปค๋„ฅ์…˜ ํ’€์ด ์‚ฌ์šฉํ•˜๊ณ ์žˆ๋Š” ์ปค๋„ฅ์…˜์ด ์ค‘๋‹จ๋˜์—ˆ๊ฑฐ๋‚˜ ๋ฌธ์ œ์ž„์„ ๋‚˜ํƒ€๋ƒ„.

PostgreSql, MySql ์—์„œ ๊ฐ๊ฐ timeout ์‹œ๊ฐ„์„ ์œ„์™€ ๊ฐ™์ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. // aws rds๋ฅผ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์—์„œ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Reader์—์„œ DB๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๊ณ ํ•˜๋”๋ผ๋„, ์ฒญํฌ๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์‹œ์ ์— step_execution์„ ์—…๋ฐ์ดํŠธํ•˜๋ฉด์„œ db๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Writer๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ์ „๊นŒ์ง€ DB๋ฅผ ์‚ฌ์šฉํ•˜์ง€์•Š๋Š”๋ฐ, ๊ทธ ์‹œ๊ฐ„์ด ์œ„ timeout (60์ดˆ)๋ฅผ ์ดˆ๊ณผํ–ˆ์„ ๋•Œ๋งŒ ๊ฐ„ํ—์ ์œผ๋กœ ๋ฐœ์ƒํ–ˆ๋˜ ๊ฒƒ์ด์—ˆ๋‹ค.

  1. chunk ๋‹จ์œ„ ์ž‘์—… ์‹œ์ž‘ ์‹œ, ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ์œผ๋กœ ์ธํ•ด HikariCP ์—์„œ ์ปค๋„ฅ์…˜์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ปค๋„ฅ์…˜์„ ์ด๋ฏธ ๋ฐ›์•˜๋”๋ผ๋„ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉด DB์— ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  3. ์ฆ‰ DB ์ž…์žฅ์—์„œ๋Š” connection์ด ์œ ํ›„ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. idle_in_transaction_session_timeout (60์ดˆ)๊ฐ€ ์ง€๋‚˜๋ฉด ์ปค๋„ฅ์…˜์ด ๋Š๊น๋‹ˆ๋‹ค.
  4. ์‹ ๋‚˜๊ฒŒ Reader์™€ Processor๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์„œ 60์ดˆ๋ฅผ ์ง€๋‚˜๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ์ด ๋•Œ HikariCP ์ปค๋„ฅ์…˜์€ ์‚ด์•„์žˆ์ง€๋งŒ, DB ์ปค๋„ฅ์…˜์€ ์ด๋ฏธ ๋Š๊ธด์ƒํƒœ์ž…๋‹ˆ๋‹ค.
  5. ์ดํ›„ writer์—์„œ ์ž‘์—…์„ ํ•˜๋“ , spring batch์—์„œ stepExecution์„ ์ €์žฅํ•˜๋ ค๊ณ  ํ•˜๋“  ์ปค๋„ฅ์…˜์ด ๋Š๊ฒจ๋ฒ„๋ ธ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ฌด๊ฒƒ๋„ ์•ˆ๋ฉ๋‹ˆ๋‹ค.



 

๐Ÿซ  ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์ง€? - ResourcelessTransactionManager

 

๋‹คํ–‰ํžˆ๋„ ์ด๋Ÿฐ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด Spring Batch์—์„  ResourcelessTransactionManager ๋ผ๋Š”๊ฑธ ๋”ฐ๋กœ ์ œ๊ณตํ•œ๋‹ค.

@Bean
public Step step() throws Exception {
    return stepBuilderFactory.get("myStep")
            .<ParseData, Item>chunk(chunkSize)
            .reader(reader())
            .processor(processor())
            .writer(writer())
            .transactionManager(new ResourcelessTransactionManager()) // No Transaction
            .build();
}

 

์‰ฝ๊ฒŒ๋งํ•˜๋ฉด SpringBatch์—์„œ ํ•„์š”์—†๋Š” ํŠธ๋žœ์žญ์…˜์„ ๊ฑธ์ง€์•Š๊ณ  StepExceution๊ณผ ๊ฐ™์€ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
(์ˆ˜์ •) JobRepository๋กœ ๋ฉ”ํƒ€์ •๋ณด๋Š” ์ €์žฅํ•œ๋‹ค. ์ฒญํฌ๋‹จ์œ„๋กœ ํŠธ๋žœ์žญ์…˜๋งŒ ๊ฑธ์ง€ ์•Š์„ ๋ฟ์ด๋‹ค. ResourceLessTxManager ์ฝ”๋“œ๋ฅผ ์—ด์–ด๋ณด๋ฉด ์ดํ•ด๊ฐ€ ์‰ฌ์šธํ…๋ฐ, ๊ทธ๋ƒฅ ๋™์ž‘ํ•˜๋Š” ์ฒ™ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค.

์ฆ‰ ๋™์ž‘์—๋Š” ๋ณ€ํ•จ์ด ์—†๋Š”๋ฐ ๋‹จ์ง€ [์ฒญํฌ๋‹จ์œ„ ํŠธ๋žœ์žญ์…˜]์ด ์—†์„ ๋ฟ์ด๋‹ค. ๋ฐฐ์น˜๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜์ฝ”๋“œ์—์„œ Repository๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผ


๋ฌผ๋ก  ๋งŒ๋Šฅ์€ ์•„๋‹ˆ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹น์—ฐํ•œ๊ฑฐ์ง€๋งŒ Reader์—์„œ ๋‚ด ์ฝ”๋“œ๋กœ ์ปค๋„ฅ์…˜์„ ์ง์ ‘ ์—ด๊ณ  ์•ˆ์“ฐ๋‹ค๊ฐ€ Writer์—์„œ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์ฒ˜์Œ๊ณผ ๋™์ผํ•˜๊ฒŒ timeout์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Reader ์—์„œ๋งŒ DB๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Writer์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ (์„ธ์…˜์ด ๋‹ซํ˜€๋„ ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ์ง€ ์•Š์œผ๋‹ˆ ์•„๋ฌด ๋ฌธ์ œ ์—†๋‹ค.)
  • Writer ์—์„œ๋งŒ DB๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Reader์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ (์„ธ์…˜ ์ž์ฒด๊ฐ€ writer ์‹œ์ ์— ์—ด๋ฆฐ๋‹ค.)

https://kwonnam.pe.kr/wiki/springframework/batch

 

 

 

 

 

2๏ธโƒฃ ๊ฐ€๋Šฅํ•˜๋ฉด JPA ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž.

๋ฐฐ์น˜์ž‘์—…์„ ํ•˜๋ฉด์„œ ๋ณ„ ์ƒ๊ฐ์—†์ด ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ Domain ์ฝ”๋“œ์˜ JpaRepository๋ฅผ ์ด์šฉํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œ ์ž‘์—…๋“ฑ์—๋Š” JpaCursorItemReader๋“ฑ์„ ํ™œ์šฉํ•˜๊ณค ํ–ˆ์—ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์•„๋ฌด ๋ฌธ์ œ ์—†์—ˆ์ง€๋งŒ ์ž‘์—…๋Ÿ‰์ด ์ปค์ง€๋‹ˆ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ์ด ์—†๋Š”๋ฐ๋„ DB CPU ์‚ฌ์šฉ๋ฅ ์ด ๋†’๋‹ค๋Š” ์•Œ๋žŒ์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

 

 

๋ฐฐ์น˜์ž‘์—…์˜ ํŠน์„ฑ์ƒ ChunkSize ๋งŒํผ I/O๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ๋œ๋‹ค. JPA๋Š” ๋”ํ‹ฐ์ฒดํ‚น์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ๊ฑด์œผ๋กœ์กฐํšŒํ•ด์„œ Writer๊ฐ€ ์ง„ํ–‰๋œ๋‹ค. ์ฆ‰ ๊ฐฏ์ˆ˜๋งŒํผ select ์š”์ฒญ์„ db์— ๋‚ ๋ฆฌ๊ฒŒ ๋œ๋‹ค๋Š” ๋ง

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๊ฑด ๊ฐ„๋‹จํ–ˆ์—ˆ๋‹ค. JdbcReader๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Projections ์„ ์ด์šฉํ•ด์„œ JPA ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฑฐ์น˜์ง€์•Š๊ณ  ๋ฐ”๋กœ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋‹ˆ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค. ๋˜ JPA๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋‹ˆ ์ฟผ๋ฆฌ๋กœ update .. where in (ids) ์™€ ๊ฐ™์ด ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋Š”๊ฑด ๋ค

  • update ์ž‘์—… ์ž์ฒด๊ฐ€ ๋งŽ์•„์„œ ๋ณ€๊ฒฝ์ด ํž˜๋“ค๋‹ค๋ฉด DB I/O ์ž‘์—…์„ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” jdbc executeBatch๋ฅผ ํ™œ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ๊ทธ ์™ธ https://tech.kakaopay.com/post/ifkakao2022-batch-performance-read/ ๊ฐ™์€ ๋ฐฐ์น˜ ์„ฑ๋Šฅ ํŠœ๋‹์„ ๊ฐ™์ด ์ฐธ๊ณ ํ•˜์ž.

https://tech.kakaopay.com/post/spring-batch-performance/

 

 

 

 

3๏ธโƒฃ Domain ๋กœ์ง์ด๋‚˜ ๋ณ€ํ•˜๋Š” ๊ฐ’(time, option)์€ ๋ฐฐ์น˜์—์„œ ๋ถ„๋ฆฌํ•˜์ž.

์˜ˆ๋ฅผ ๋“ค์–ด ๋งค๋‹ฌ 1์ผ์— ์‹คํ–‰ํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๋ฐฐ์น˜๋ผ์„œ LocalDateTime.now() ๊ฐ™์ด ์ฝ”๋“œ์— ๋ฐ•์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ทธ๋Ÿฌ์ง€ ์•Š๋Š”๊ฑธ ๊ถŒ์žฅํ•œ๋‹ค.

  • ์„œ๋ฒ„์‹œ๊ฐ„์ด๋‚˜ ์‹คํ–‰ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๊ฐ’์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • ๋‹น์žฅ์€ ๋งค๋‹ฌ 1์ผ๋งŒ ์‹คํ–‰ํ•  ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์ƒํ™ฉ์— ๋”ฐ๋ผ (ํ˜น์€ ๋ฐฐ์น˜๊ฐ€ ์‹คํŒจํ•ด์„œ) ๋‹ค๋ฅธ ๋‚ ์งœ์— ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์˜ต์…˜๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋ฐฐ์น˜๋ฅผ ์ƒˆ๋กœ ๋ฐฐํฌํ•ด์•ผํ•œ๋‹ค.

์ฝ”๋“œ์—์„œ ๋ฐ”๋กœ ๊ตฌํ•  ์ˆ˜ ์žˆ๋”๋ผ๋„ ์œ„์™€ ๊ฐ™์€๊ฑธ ๊ณ ๋ คํ•ด์„œ JobParameters ์œผ๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜์ž.

@Value("#{jobParameters[batchDate]}") batchDate: String,
@Value("#{jobParameters[apiKey]}") apiKey: String

 

๊ฐ™์€ ๋งฅ๋ฝ์œผ๋กœ ๊ตณ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด Batch์—์„œ ๊ฐ€๋Šฅํ•˜๋ฉด Domain์— ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ณ„๋„์˜ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค.  ๋‘ ๊ฐœ์˜ ๋ณ€๊ฒฝ ์‹œ์ ์ด ๋‹ค๋ฅธ๋ฐ ๋ฐฐํฌ๋ฅผ ๊ฐ™์ดํ•ด์•ผํ•ด์„œ ์• ๋งคํ•ด์ง€๊ธฐ๋„ ํ•˜๊ณ  ๋„๋ฉ”์ธ์—์„œ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์„œ ๋ถˆํ•„์š”ํ•œ ๊ณผ์ •์„ ์ค„์ด๊ณ  ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

https://techblog.woowahan.com/2637/

 

 

 

 

4๏ธโƒฃ Update, Delete ๋ฐฐ์น˜์ž‘์—…์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ˆ„๋ฝ๋˜์ง€ ์•Š๊ฒŒ ์ฃผ์˜ํ•˜์ž

์ •๋ง ๊ฐ„๋‹จํ•œ๊ฑด๋ฐ Update, Delete ๋ฐฐ์น˜ ์ž‘์—…์˜ ๊ฒฝ์šฐ ํŽ˜์ด์ง•์ด ๊นจ์ง„๋‹ค๋Š” ๊ฑธ ์ ˆ๋Œ€ ์žŠ์ง€๋ง์ž. ๋ณ„๊ฑฐ ์•„๋‹Œ๋ฐ ๋ง‰์ƒ ๋Œ๋ ค๋ณด๋ฉด ์ž˜ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ ๊ฐ™์ด ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋ฅด๊ณ  ์žˆ์œผ๋ฉด ์ •๋ง ์ฐพ๊ธฐ ํž˜๋“ค๋‹ค ๐Ÿฅฒ

  • pagingReader๋กœ 10๊ฐœ๋ฅผ ์ฝ์–ด์˜ด
  • read 10๊ฐœ -> writer 10 ๊ฐœ
  • writer 10๊ฐœ๋ฅผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๊ฐ„์— ๋ช‡ ๊ฐœ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ๋ฐ˜ํ™˜๋จ



์ œ์ผ ํŽธํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ CursorReader๋กœ ์ฝ์–ด๋“ค์ด๊ฑฐ๋‚˜, ๊ทธ๊ฒŒ ์–ด๋ ต๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด pageSize=0์œผ๋กœ ๊ณ ์ •ํ•ด์„œ ์ฝ์œผ๋ฉด ๋œ๋‹ค.

๋‹จ pageSize ๋ฅผ ๊ณ ์ •ํ•œ ๊ฒฝ์šฐ ๋ชจ๋“  ์•„์ดํ…œ์ด ์™„๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ฐฐ์น˜๊ฐ€ ๋Š๋‚˜์ง€ ์•Š๋Š” ๋‹ค๋Š”๊ฑธ ๋ช…์‹ฌํ•˜์ž.

@Bean
@StepScope
public ItemReader<Order> itemReader() {
    MyBatisPagingItemReader<Order> itemReader = new MyBatisPagingItemReader<Order>() {
        @Override
        public int getPage() {
            return 0;
        }
    };
    itemReader.setQueryId("query-name");
    itemReader.setPageSize(PAGE_SIZE);
    itemReader.setSqlSessionFactory(sqlSessionFactory);
    return itemReader;
}
// Kotlin์ด๋ฉด ์ด๋ ‡๊ฒŒ.
val reader: JpaPagingItemReader<Group> = object : JpaPagingItemReader<Group>() {
    override fun getPage(): Int {
        return 0
    }
}.apply {
    pageSize = batchPageSize
    setName("reader")
    setEntityManagerFactory(entityManagerFactory)
    setQueryString( "SELECT * FROM Group")
}

 

 

 

 

๐Ÿ“š ๋ ˆํผ๋Ÿฐ์Šค

https://jojoldu.tistory.com/526

https://kwonnam.pe.kr/wiki/springframework/batch

https://pkgonan.github.io/2018/04/HikariCP-test-while-idle

https://kakaocommerce.tistory.com/45

https://devocean.sk.com/blog/techBoardDetail.do?ID=164123

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B0%B0%EC%B9%98/dashboard

 

์Šคํ”„๋ง ๋ฐฐ์น˜ ๊ฐ•์˜ - ์ธํ”„๋Ÿฐ

์ดˆ๊ธ‰์—์„œ ์ค‘~๊ณ ๊ธ‰์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ์Šคํ”„๋ง ๋ฐฐ์น˜์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ API ์‚ฌ์šฉ๋ฒ•๊ณผ ๋‚ด๋ถ€ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ๋ฅผ ์‹ฌ๋„์žˆ๊ฒŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์Šคํ”„๋ง ๋ฐฐ์น˜ ๊ฐ ๊ธฐ๋Šฅ์˜ ํ๋ฆ„๊ณผ ์›๋ฆฌ๋ฅผ ํ•™์Šตํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋ฅผ ๋ฐ”ํƒ•์œผ

www.inflearn.com

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ