JpaPagingItemReader 사용 시 주의할 점

러블리소피·2025년 1월 7일

✅ JpaPagingItemReader 사용 시 주의할 점

1. 읽기 전용 트랜잭션 설정 (readOnly = true)

  • JpaPagingItemReader는 대용량 데이터를 읽어올 때 사용하는데, 이때 JPA의 변경 감지 기능이 불필요하게 동작하지 않도록 readOnly 트랜잭션을 설정해야 성능이 향상됩니다.
  • Spring Batch에서는 Step에 트랜잭션을 설정할 수 있습니다.
@Bean
public Step exampleStep() {
    return stepBuilderFactory.get("exampleStep")
        .<Entity, Entity>chunk(1000)
        .reader(jpaPagingItemReader())
        .writer(jpaItemWriter())
        .transactionAttribute(new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, true))
        .build();
}

2. 페이징 정렬 조건 설정

  • JpaPagingItemReader는 내부적으로 LIMIT과 OFFSET을 사용하여 페이징 처리를 합니다.
  • 데이터가 많아질수록 성능이 떨어질 수 있으므로 반드시 ORDER BY를 설정해야 합니다.
  • @Id 필드로 정렬하는 것이 일반적입니다.
JpaPagingItemReader<Entity> reader = new JpaPagingItemReaderBuilder<Entity>()
    .name("jpaPagingItemReader")
    .entityManagerFactory(entityManagerFactory)
    .queryString("SELECT e FROM Entity e ORDER BY e.id ASC")
    .pageSize(1000) // 페이징 크기
    .build();

3. pageSize와 chunkSize 일치 여부 확인

  • JpaPagingItemReader의 pageSize와 chunkSize가 다를 경우 chunk 처리에서 예상치 못한 데이터 누락 또는 중복이 발생할 수 있습니다.
  • 일반적으로 pageSize와 chunkSize를 동일하게 설정하는 것이 좋습니다.
.pageSize(1000) // 동일하게 설정
chunk(1000) // 동일하게 설정

4. Pageable 기반 쿼리와의 차이점

  • JpaPagingItemReader는 Spring Data JPA의 Pageable 쿼리와 달리 현재 페이지에 있는 데이터가 변경되어도 다른 페이지에는 영향을 미치지 않는 방식으로 동작합니다.
  • 따라서 배치 실행 중 데이터가 변경되면 일부 데이터가 누락되거나 중복될 수 있으므로, 배치 실행 시점에 데이터 변경이 발생하지 않도록 주의해야 합니다.

✅ chunk 기반 처리 시 주의할 점

1. 데이터 중복/누락 문제

  • 배치 실행 중 데이터가 변경되면 JpaPagingItemReader가 OFFSET을 사용하기 때문에 중복 또는 누락된 데이터가 발생할 수 있습니다.

해결 방법:

  • 배치 시작 전에 데이터를 고정하기 위해 스냅샷 테이블을 사용하는 것이 좋습니다.
  • 또는 WHERE 절에 시간 조건을 추가하여 특정 기간 내의 데이터만 처리하도록 설정합니다.
SELECT e FROM Entity e WHERE e.createdAt >= :startTime AND e.createdAt < :endTime ORDER BY e.id ASC

2. 페이징 중간에 데이터 삭제 주의

  • 배치가 실행 중인 동안 데이터가 삭제되면 OFFSET 값이 밀려서 일부 데이터가 누락될 수 있습니다.
  • 이를 방지하려면 WHERE 조건을 강화하거나, 삭제된 데이터를 배제할 수 있도록 조건을 추가합니다.

✅ 트러블슈팅 사례

✅ 최적화 팁

1. 스냅샷 테이블 사용

  • 배치 실행 전 스냅샷 테이블을 생성하여 고정된 데이터를 사용하면 중복/누락 문제를 방지할 수 있습니다.

2. 페이징 대신 Cursor 사용

  • 대용량 데이터 처리를 위해 페이징 대신 Cursor 방식으로 처리하는 것도 고려할 수 있습니다. Cursor 방식은 데이터를 한 번에 로드하지 않고 스트리밍 방식으로 처리합니다.

✅ 예제 코드 (최적화된 JpaPagingItemReader 설정)

@Bean
public JpaPagingItemReader<Entity> jpaPagingItemReader(EntityManagerFactory entityManagerFactory) {
    return new JpaPagingItemReaderBuilder<Entity>()
        .name("jpaPagingItemReader")
        .entityManagerFactory(entityManagerFactory)
        .queryString("SELECT e FROM Entity e WHERE e.createdAt >= :startTime AND e.createdAt < :endTime ORDER BY e.id ASC")
        .parameterValues(Map.of("startTime", startTime, "endTime", endTime))
        .pageSize(1000)
        .build();
}

이렇게 설정하면 데이터 중복/누락 문제를 줄이고, 대량 데이터를 효율적으로 처리할 수 있습니다. 😊

profile
발전하는 개발자가 되고싶어요

0개의 댓글