스프링 부트와 JPA와 함께하는 배치 최적화

스프링 부트와 JPA와 함께하는 배치 최적화

JPA의 기본 사항을 정리한다.

스프링부트 + 배치

배치는 DB/네트워크 호출 횟수를 크게 줄여 성능 향상을 할 수 있다.

EX) 개별적인 1000건의 등록 처리는 1000번의 네트워크 호출이 필요하지만, 30개씩 34번의 호출로 처리할 수 있다.

또한 개별적인 배치는 작동이후 즉각적인 Commit이 되어야 유리하다. 장기 실행 트랜잭션을 피하고 장애가 발생한 경우 롤백이 이전 커밋에 영향을 미치지 않는다.


INSERT

설정 고려 Config 정보

spring.jpa.properties.hibernate.jdbc.batch_size = 30 설정 추가한다. (권장 5~30)

한 번에 처리할 batch Size를 설정한다.

spring.datasource.hikari.data-source-properties-reWriteBatchedInserts=true

SQL문이 문자열 버퍼로 재작성 되어 1건의 SQL로 변환된다.

  • MYSQL은 그냥 사용하면 된다.

  • POSTGRESQL은 식별자의 @GeneratedValue strategy 속성에 대해 주의해야 한다.

    • IDENTITY의 경우 개별 sequence next_val 쿼리를 매 번 발생시키기 때문에 AUTO나 SEQUENCE 메모리 속성을 사용해야 한다.

spring.jpa.properties.hibernate.order_inserts=true

1회의 배치는 1개의 Table만 사용할 수 있다. batch_size의 개수와 상관없이 Author를 적재하고 Book을 적재하면, 2번이 작동되는 개념이다. OneToMany 연관관계를 함께 적재하는 경우 사용하면 한 테이블씩 배치 작업을 진행한다.

실사용 정보

배치 동시 실행 방법 3가지

엔티티 배치 프로세스 속도가 중요한 경우, 배치처리를 동시에 실행하는 것을 고려할 수 있다.

  • Executors
  • 포크/조인 프레임워크
  • CompletableFuture

UPDATE

설정 고려 Config 정보

spring.jpa.properties.hibernate.jdbc.batch_versioned_date=true

업데이트되어아 햘 엔티티에 @Version 이 지정된 경우 설정. 근데 하이버네이트5부터는 기본적으로 활성화되어있다.

spring.jpa.properties.hibernate.order_updates=true

1:N 관계에서 여러 Table이 사용되는 경우 사용하면 한 테이블에 대한 배치가 끝나고 다른 배치를 실행하여 총 실행 배치를 줄여준다. (배치는 개수와 별개로 1Table에 1개씩 가능하다.)

실 사용 정보

bulk update 방법

//전체 BulkUpdate
@Transactional
@Modifying(flushAutomatically = true, clearAuthomatically = true)
@Query(value = "UPDATE Author a SET a.age = a.age + 1, a.version = a.version")//version을 명시적으로 증가시킨다.
int updateInBulk();

//특정 조건의 Authors만 Update하는 경우
@Transactional
@Modifying(flushAutomatically = true, clearAuthomatically = true)
@Query(value = "UPDATE Author a SET a.age = a.age + 1, a.version = a.version"
       + "WHERE a IN ?1")
int updateInBulk(List<Author> authors);

//실사용 Service
@Transactional
public void updateBulkAuthrosAndBooks(){
    authorRepository.updateInBulk();
    bookRepository.updateInBulk();
}

//특정 조건 실사용 Service
@Transactional
public void updateAuthorsGtAgeAndBooks(){
    List<Author> authors = authorRepository.findGtGivenAge(40);
    
    authorRepository.updateInBulk(authors);
    bookRepository.updateInBulk(authors);
}

DELETE

Config

  • MYSQL은 다음과 같은 설정이 필요하다.
    • jdbc:mysql://localhost:3306/bookstoredb?cachePreStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true
  • spring.jpa.properties.hibernates.jdbc.batch_size=30
  • spring.jpa.properties.hibernate.jdbc.batch_versioned_date=true

delete는 deleteAllInBatch(), deleteInBatch(Iterable entities) 를 배치 삭제로 사용하지 않아야 한다.
why? 낙관적 잠금 메커니즘의 이점이 없는 벌크 작업을 트리거 하고 1차캐시가 db와 동기화되지 않는다.
대신 `deleteAll()`, `deleteAll(Iterable<? extends T> entities)` 또는 `delete(T entity)` 를 사용해야 한다.