๐Ÿงช

Spring Batch JpaCursorItemReader์— ๋Œ€ํ•˜์—ฌ

์ตœ๋ฏผ์„ยท2025-11-04

๐Ÿงช Spring Batch JpaCursorItemReader์— ๋Œ€ํ•˜์—ฌ

Spring Batch์—์„œ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค๋ค„๋ณธ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ํ•œ ๋ฒˆ์ฏค ๊ณ ๋ฏผํ–ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
โ€œJPA ๊ธฐ๋ฐ˜์œผ๋กœ ์ปค์„œ(Cursor)์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์„๊นŒ?โ€
์˜ค๋Š˜์€ ๊ทธ ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ์ธ JpaCursorItemReader ์˜ ์—ญ์‚ฌ์™€, ์™œ HibernateCursorItemReader๊ฐ€ ์‚ฌ๋ผ์กŒ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  MySQL๊ณผ PostgreSQL์˜ ์ปค์„œ ๋™์ž‘ ์ฐจ์ด๊นŒ์ง€ ์‹ค์ œ ์‹คํ—˜ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


1. JpaCursorItemReader์˜ ์—ญ์‚ฌ

1.1 ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ

JpaCursorItemReader๋Š” Spring Batch 4.3 (2020๋…„๊ฒฝ) ์— ๋„์ž…๋œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
๊ทธ ์ด์ „๊นŒ์ง€๋Š” JPA ํ™˜๊ฒฝ์—์„œ ์ปค์„œ ๊ธฐ๋ฐ˜ ๋ฐฐ์น˜๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด Hibernate์˜ ScrollableResults๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, PagingItemReader๋ฅผ ์šฐํšŒ์ ์œผ๋กœ ์ด์šฉํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Paging ๋ฐฉ์‹์€ offset ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง• ์ฟผ๋ฆฌ ํŠน์„ฑ์ƒ ํฐ ๋ฐ์ดํ„ฐ์…‹์—์„œ๋Š” ์„ฑ๋Šฅ์ด ๊ธ‰๊ฒฉํžˆ ์ €ํ•˜๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ Spring ํŒ€์€ โ€œJPA ํ™˜๊ฒฝ์—์„œ๋„ ์ปค์„œ์ฒ˜๋Ÿผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์žโ€๋Š” ๋ชฉํ‘œ๋กœ JpaCursorItemReader๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.


1.2 ๋‚ด๋ถ€ ๋™์ž‘ (์ดˆ๊ธฐ ๋ฒ„์ „)

์ดˆ๊ธฐ JpaCursorItemReader๋Š” ์ด๋ฆ„๋งŒ Cursor์˜€์„ ๋ฟ, ์‹ค์ œ DB ์ปค์„œ(fetchSize ๊ธฐ๋ฐ˜ streaming) ๊ฐ€ ์•„๋‹Œ JPA EntityManager์˜ ResultList ์ „์ฒด ๋กœ๋”ฉ ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค.
์ฆ‰, ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์•„๋ž˜ ๋กœ์ง์ด ๋ฐ˜๋ณต๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Query query = entityManager.createQuery("SELECT e FROM Entity e");
List<Entity> results = query.getResultList(); // ์ „์ฒด fetch

๊ฒฐ๊ตญ โ€œ์ปค์„œ์ฒ˜๋Ÿผ ์ƒ๊ฒผ์ง€๋งŒ ์ „ํ˜€ ์ปค์„œ๊ฐ€ ์•„๋‹ˆ์—ˆ๋˜โ€ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด ๋•Œ๋ฌธ์— ์ˆ˜์‹ญ๋งŒ ๊ฑด ์ด์ƒ ์ฒ˜๋ฆฌ ์‹œ ํž™ ๋ฉ”๋ชจ๋ฆฌ ๊ธ‰๋“ฑ โ†’ OutOfMemoryError ๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.


1.3 ์ „ํ™˜์ : Spring Batch 5.x (2024๋…„)

Spring Batch 5.2.0-M1๋ถ€ํ„ฐ JpaCursorItemReader๋Š” JPA 2.2์˜ getResultStream() API๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์ด์ œ Hibernate 5.5+ ํ™˜๊ฒฝ์—์„œ๋Š” ์‹ค์ œ๋กœ Streaming + FetchSize ์กฐํ•ฉ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ” ์ฆ‰, Query.getResultStream() โ†’ JDBC ๋“œ๋ผ์ด๋ฒ„์˜ fetchSize๊นŒ์ง€ ๋‚ด๋ ค๊ฐ€๋ฉฐ,
๋“œ๋ผ์ด๋ฒ„ ์ˆ˜์ค€์—์„œ ์ปค์„œ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด ์—ญ์‹œ JPA ๊ตฌํ˜„์ฒด์™€ DB ๋“œ๋ผ์ด๋ฒ„์˜ ์ง€์› ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
MySQL์€ ์ด ๊ธฐ๋Šฅ์„ ์™„์ „ํžˆ ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ, PostgreSQL์€ ์ƒ๋‹นํžˆ ์™„์„ฑ๋„ ๋†’๊ฒŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ด ๊ธ€์„ ์“ฐ๊ฒŒ ๋œ ์ด์œ ์ด๊ธฐ๋„ ํ•œ๋ฐ์š”, PostgreSQL์ด ์ปค์„œ ๊ธฐ๋Šฅ์„ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ง€์›ํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ๋‹ฌ๋ฆฌ, MySQL์—์„œ๋Š” ๋ณ„๋„์˜ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


2. HibernateCursorItemReader ํ๊ธฐ ์ด์œ 

Spring Batch 3.x ~ 4.0 ์‹œ์ ˆ์—๋Š” HibernateCursorItemReader๊ฐ€ ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ํ๊ธฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

2.1 ํ๊ธฐ ์‹œ์ 

  • Spring Batch 5.0 ์—์„œ Deprecated
  • Spring Batch 5.2 ์—์„œ ์™„์ „ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

2.2 ํ๊ธฐ ์‚ฌ์œ 

  1. Hibernate ์ „์šฉ์ด๋ผ๋Š” ๊ตฌ์กฐ์  ํ•œ๊ณ„
    โ†’ JPA ํ‘œ์ค€์ด ์•„๋‹ˆ๋ฉฐ, Hibernate API ๋ฒ„์ „์— ๋”ฐ๋ผ ๊นจ์ง€๊ฑฐ๋‚˜ ๋™์ž‘์ด ๋ถˆ์•ˆ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. Session ๊ด€๋ฆฌ ์ด์Šˆ
    โ†’ ScrollableResults ์‚ฌ์šฉ ์‹œ Session ์œ ์ง€ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ ธ DB Connection Pool์ด ์‰ฝ๊ฒŒ ๊ณ ๊ฐˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  3. JPA ํ‘œ์ค€ํ™” ํ๋ฆ„
    โ†’ JPA 2.2์—์„œ getResultStream()์ด ๋„์ž…๋˜๋ฉฐ, HibernateCursorItemReader๋Š” ์ค‘๋ณต ๊ธฐ๋Šฅ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  4. Spring Batch ๋ชจ๋“ˆ ์ •๋ฆฌ ์ •์ฑ…
    โ†’ Hibernate ์ „์šฉ Reader๋Š” ํ‘œ์ค€ํ™” ์ •์ฑ…์— ๋งž์ง€ ์•Š์•„ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, Hibernate ์ „์šฉ์—์„œ JPA ํ‘œ์ค€ ๊ธฐ๋ฐ˜(JpaCursorItemReader)์œผ๋กœ ๋ฐฉํ–ฅ์ด ์˜ฎ๊ฒจ๊ฐ„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.


3. MySQL vs PostgreSQL: ์ปค์„œ ํŠธ๋ฆฌ๊ฑฐ ๋ฐฉ์‹์˜ ์ฐจ์ด

์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์นญ์€ ๋‹จ์ˆœํžˆ โ€œfetchSize๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹คโ€ ์ˆ˜์ค€์ด ์•„๋‹™๋‹ˆ๋‹ค.
JDBC ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์–ด๋–ค ์‹œ์ ์— ์ปค์„œ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ• ์ง€์— ๋”ฐ๋ผ ์™„์ „ํžˆ ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ MySQL PostgreSQL
JDBC ์˜ต์…˜ useServerPrepStmts, useCursorFetch=true, defaultFetchSize ํ•„์š” defaultFetchSize๋งŒ์œผ๋กœ ์ถฉ๋ถ„
์‹คํ–‰ ๋ฐฉ์‹ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์ปค์„œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•จ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„ ์ปค์„œ๋ฅผ ์ง€์›
์ฟผ๋ฆฌ ์ „์†ก ๊ตฌ์กฐ ์ „์ฒด ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฒ„ํผ๋ง ํ›„ ์ „๋‹ฌ (์˜ต์…˜ ๋น„ํ™œ์„ฑ ์‹œ) ์„œ๋ฒ„ ๋‚ด๋ถ€ ์ปค์„œ ์œ ์ง€ ํ›„ ํ•„์š”ํ•  ๋•Œ๋งŒ fetch
์ฒ ํ•™ ๋‹จ์ผ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋น ๋ฅด๊ฒŒ ๋ฐ˜ํ™˜ ๋Œ€๊ทœ๋ชจ ์ŠคํŠธ๋ฆฌ๋ฐ๊ณผ ํŠธ๋žœ์žญ์…˜ ์•ˆ์ •์„ฑ ์ค‘์‹ฌ
์‹ค์งˆ์  ์ปค์„œ ๋ชจ๋“œ ์˜ต์…˜ ์—†์œผ๋ฉด โ€œ๊ฐ€์งœ ์ปค์„œโ€ ํ•ญ์ƒ โ€œ์ง„์งœ ์ปค์„œโ€

๊ฒฐ๊ตญ MySQL์—์„œ๋Š”
useServerPrepStmts + useCursorFetch=true + defaultFetchSize=N
์„ธ ์˜ต์…˜์ด ๋ชจ๋‘ ํ•„์š”ํ•ด์•ผ ์‹ค์ œ ์ปค์„œ ๊ธฐ๋ฐ˜ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.


4. ์‹คํ—˜: MySQL์˜ Heap Memory ์ฐจ์ด

4.1 ๊ฐ„๋‹จํ•œ ๋ฐฐ์น˜ ๋ฐ์ดํ„ฐ ์ค€๋น„

MySQL JDBC๋“œ๋ผ์ด๋ฒ„๋ฅผ ์‚ฌ์šฉํ• ๋•Œ, ์˜ต์…˜ ์ง€์ • ์—†์ด๋Š” Cursor๊ธฐ๋ฐ˜ ๋ฐฐ์น˜์ฒ˜๋ฆฌ๊ฐ€ ์˜๋„๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ์„ ์ฆ๋ช…ํ•˜๊ธฐ ์œ„ํ•ด, ๊ฐ„๋‹จํ•œ ์‹คํ—˜์„ ํ•˜๋‚˜ ์ค€๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด์‹ฑ๋˜๊ธฐ ์ „์˜ ๋ฌธ์ž์—ด์„ ๊ฐ€์ง€๋Š” BeforeHash ์—”ํ‹ฐํ‹ฐ์™€, ํ•ด๋‹น ๋ฌธ์ž์—ด์„ ํ•ด์‹ฑํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” AfterHash์—”ํ‹ฐํ‹ฐ๋ฅผ ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

@Entity
public class BeforeHash {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;
    
}


@Entity
public class AfterHash {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;
    
    @OneToOne
    @JoinColumn(name = "source_id", nullable = false)
    private BeforeHash beforeHash;
    
}

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ๊ฒƒ์€ Reader ๋ฟ์ด๋ฏ€๋กœ, Processor๋‚˜ Writer๋Š” ์ƒ๋žตํ•˜๊ณ , Reader ๊ตฌ์„ฑ๋งŒ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

@Configuration
public class BeforeHashItemReaderConfig {
    
    @Bean
    @StepScope
    @Qualifier("beforeHashItemReader")
    public JpaCursorItemReader<BeforeHash> beforeHashItemReader(EntityManagerFactory emf) {
        return new JpaCursorItemReaderBuilder<BeforeHash>()
            .name("beforeHashItemReader")
            .entityManagerFactory(emf)
            .queryString(
            """
            SELECT b FROM BeforeHash b
            WHERE NOT EXISTS
                (SELECT a FROM AfterHash a WHERE a.beforeHash.id = b.id)
            ORDER BY b.id ASC
            """
            )
            .build();
    }
}

์•„์ง AfterHash๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์€ BeforeHash๋“ค์„ ๋ชจ์•„์„œ Cursor ๋ฐฉ์‹์œผ๋กœ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

Step ๋ฐ Job์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

@Configuration
public class HashJobStep {
    
    @Bean
    @Qualifier("hashStep")
    public Step hashStep(
        JobRepository jobRepository,
        PlatformTransactionManager txManager,
        @Qualifier("beforeHashItemReader")ItemReader<BeforeHash> reader,
        HashItemProcessor processor,
        @Qualifier("afterHashCreateWriter") ItemWriter<AfterHash> writer
        ) {
        return new StepBuilder("hashStep", jobRepository)
            .<BeforeHash, AfterHash>chunk(500, txManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }
    
    @Bean
    @Qualifier("hashJob")
    public Job hashJob(
        JobRepository jobRepository,
        @Qualifier("hashStep") Step hashStep
    ) {
        return new JobBuilder("hashJob", jobRepository)
                .start(hashStep)
                .build();
    }
}

500๊ฐœ์”ฉ ์ฒญํฌ๋กœ ๋ฌถ์–ด ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋กœ๋Š”, BeforeHash ํ…Œ์ด๋ธ”์— ์•ฝ 150๋งŒ๊ฐœ์˜ Row๋ฅผ ์ค€๋น„ํ•ด ๋‘์—ˆ๋Š”๋ฐ์š”, ๊ธฐ๋ณธ์ ์œผ๋กœ ํž™์— ์˜ฌ๋ผ๊ฐ€ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค(ํด๋ž˜์Šค ๋กœ๋”, String ์ƒ์ˆ˜ ํ’€ ๋“ฑ)์ด ์žˆ์–ด์„œ ์ƒ๋Œ€์ ์œผ๋กœ ํฌ๊ฒŒ ๋น„๊ต๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•ด ๋†“์•˜์Šต๋‹ˆ๋‹ค.

select count(*) from before_hash;

count_query

์ด์ œ ์‹ค์ œ Job์„ ํŠธ๋ฆฌ๊ฑฐ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

4.2 Cursor ๋น„ํ™œ์„ฑํ™” (useCursorFetch=false)

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/batch
           ?useSSL=true
           &useUnicode=true
           &maxQuerySizeToLog=999999
           &zeroDateTimeBehavior=convertToNull
           &rewriteBatchedStatements=true

๋ฐ”๋กœ ์œ„ ์„ค์ •์ด, ์ œ๊ฐ€ ์ฒ˜์Œ MySQL๋กœ Cursor ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ํ–ˆ์„ ๋•Œ์˜ ์„ค์ •์ž…๋‹ˆ๋‹ค. ๋”ฐ๋กœ useServerPrepStmts, defaultFetchSize๋‚˜ useCursorFetch์˜ต์…˜์„ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, 150๋งŒ๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ JpaCursorItemReader๋กœ ๋ฐฐ์น˜์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ, ํž™ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ ์•„๋ž˜์ฒ˜๋Ÿผ ์„ ํ˜•์ ์œผ๋กœ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ JVM์— ์ง์ ‘ ์˜ฌ๋ฆฌ๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

disable_cursor_fetch

  • ๋ฉ”๋ชจ๋ฆฌ ๊ธ‰๋“ฑ โ†’ GC ๋ฐ˜๋ณต โ†’ ์ฒ˜๋ฆฌ ์ง€์—ฐ
  • fetchSize ์„ค์ • ๋ฌด์‹œ, ์ „์ฒด ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌํ•ฉ๋‹ˆ๋‹ค.

4.3 Cursor ํ™œ์„ฑํ™”

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/batch
           ?useSSL=true
           &useUnicode=true
           &useServerPrepStmts=true
           &maxQuerySizeToLog=999999
           &zeroDateTimeBehavior=convertToNull
           &rewriteBatchedStatements=true
           &useServerPrepStmts=true # ๐Ÿ’ก ์ถ”๊ฐ€!
           &defaultFetchSize=500    # ๐Ÿ’ก ์ถ”๊ฐ€!
           &useCursorFetch=true     # ๐Ÿ’ก ์ถ”๊ฐ€!

์ด๋ฒˆ์—” ์ปค์„œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด useServerPrepStmts, defaultFetchSize, useCursorFetch ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

enable_cursor_fetch

ํž™๋ฉ”๋ชจ๋ฆฌ ์ ์œ ์œจ๋ฆฌ ๋†€๋ž๋„๋ก ์ค„์–ด๋“  ๊ฒƒ์ด ๋ณด์ด๋‚˜์š”?

  • ์ผ์ •ํ•œ ํž™ ์‚ฌ์šฉ๋Ÿ‰ ์œ ์ง€
  • ์ผ์ • chunk ๋‹จ์œ„๋กœ fetch ์ˆ˜ํ–‰
  • ์•ˆ์ •์ ์ธ Throughput ํ™•๋ณด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ •๋ฆฌ

์™„์ „ํ•œ ์ปค์„œ ๋™์ž‘์„ ๊ธฐ๋Œ€ํ•˜๋ ค๋ฉด, ์—ฌ์ „ํžˆ JPA ๊ตฌํ˜„์ฒด์™€ DB ๋“œ๋ผ์ด๋ฒ„์˜ ํ˜‘์กฐ๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

  • Hibernate 5.5+ ์ด์ƒ
  • MySQL Connector/J 8.0+
  • useServerPrepStmts + useCursorFetch=true + defaultFetchSize(๊ธฐ๋ณธ๊ฐ’ 500) ๋ช…์‹œ

Cursor์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ ค๋ฉด, PreparedStatement๋ผ๋Š” ๊ฒƒ์„ ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•˜๋Š”๋ฐ์š”, ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€ ๋А๋‚Œ์ด ์žˆ์ง€๋งŒ ๋‹ค์Œ ์•„ํ‹ฐํด์—์„œ๋Š” ์ด์— ๊ด€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.