본문 바로가기
spring & java

[JPA] batch insert 적용

by do5do 2022. 11. 19.

batch insert는 단건 insert가 아닌, insert를 모아서 하는 multi insert를 의미한다.

jpa에서 객체를 데이터베이스에 저장할 때 save() 대신 saveAll()을 한다고해서 멀티 insert가 되는건 아니었다. 별도의 설정이 필요했다.

 

설정 추가

데이터베이스별로 설정 방법이 다르다.

(Oracle은 못찾았다..)

// 공통 설정
spring.jpa.properties.hibernate.jdbc.batch_size=1000
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

// mysql은 아래 설정 추가
spring.datasource.hikari.data-source-properties.rewriteBatchedStatements=true

// postgre는 아래 설정 추가
spring.datasource.hikari.data-source-properties.rewriteBatchedInserts=true
  • batch_size: insert/update를 실행할 크기 설정
  • order_inserts와 updates: 엔티티별로 insert와 update를 정렬하여 실행시키는 옵션
  • rewrite 옵션: insert 구문을 재작성하는 옵션

 

주의 사항

1) id 채번 방식이 GenerationType.IDENTITY이면 batch insert가 불가능하다.

GenerationType.SEQUENCE 또는 GenerationType.TABLE로 설정 변경이 필요하다.

 

GenerationType.TABLE은 PK를 별도 테이블로 관리하는 건데 관리 할 테이블이 늘어나기 때문에 번거롭다.

그래서 sequence 전략을 추천하는데, 만약 이를 지원하지 않는 DB라면(ex. Mysql) JPA가 아니라 jdbc를 사용하여 bulk insert를 수행하는게 속도 측면에서 더 우수하다고 한다.

 

  • GenerationType.IDENTITY 전략은 데이터 입력 시 엔티티를 먼저 저장하고 식별자를 조회해서 할당하는 방식이다.
  • GenerationType.SEQUENCE 전략은 데이터 입력 시 sequence를 먼저 조회하고(sequence 조회 쿼리 발생) 엔티티에 할당하여 저장한다.

 

2) id 채번 방식 → batch 채번으로 변경

id 채번을 sequence 타입으로 변경한다고 해서 batch insert가 빠른 속도로 수행되는 것은 아니다.

insert는 배치로 수행하지만 id를 채번할 때는 하나 씩 채번한다면 의미가 없기 때문이다.

 

그래서 id도 batch size만큼 미리 채번하도록 하는 설정이 필요하다.

 

 

id batch 채번 설정

1) hibernate의 GenericGenerator를 사용하여 Optimizer를 직접 지정하여 사용

pooled 또는 pooled-io optimizer를 사용하면 시퀀스를 increment_size만큼 채번하여 메모리에 올려둔다.

즉, 한번의 쿼리로 1000개의 시퀀스를 가지고 오는 것이다.

optimizer 설정 참고 - https://techblog.woowahan.com/2663/

// entity class
...

public class Molecule {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "uniprot_source_seq")
    @GenericGenerator(
            name = "uniprot_source_seq",
            strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
            parameters = {
                    @Parameter(name = "sequence_name", value = "uniprot_source_seq"),
                    @Parameter(name = "optimizer", value = "pooled"),
                    @Parameter(name = "initial_value", value = "1"),
                    @Parameter(name = "increment_size", value = "1000")
            }
    )
    private Long id;
    
    ...
  }

 

Oracle DB 사용 시 위 설정으로 실행했을 때 DB의 increment value 값과 @Parameter로 설정한 increment value값이 다르다는 에러가 발생했고, 끝내 해결하지 못했기때문에 다른 방법을 찾았다. ㅠㅠ

(Oracle db에서 sequence를 삭제하고 새로 생성할 때 increment by 1000;을 해도 db의 increment가 1이라는 동일한 에러 발생)

 

 

2) id batch 채번 외부 라이브러리 사용

batch insert를 적용하는 시점(Oracle DB를 사용할 때)에는 1번에 대한 해결 방법으로 외부 라이브러리를 사용하였다.

 

 

의존성 추가 및 설정

사용중인 hibernate 버전에 맞춰서 적용한다.

implementation group: 'com.vladmihalcea', name: 'hibernate-types-55', version: '2.17.0'
@GenericGenerator(
        name = "uniprot_seq",
        strategy = "com.vladmihalcea.hibernate.id.BatchSequenceGenerator",
        parameters = {
                @Parameter(name = "sequence", value = "uniprot_seq"),
                @Parameter(name = "fetch_size", value = "1000")
        }
)

 

실행 쿼리

SELECT uniprot_seq.nextval FROM dual CONNECT BY rownum <= 1000;

 

장점

  • DB의 sequence increment를 바꾸지 않아도 된다. → sequence increment 1로 유지 가능

 

결과

약 5천건의 데이터 입력 성능 60배 향상

  • batch insert 적용 전 - 3분 소요
  • 적용 후 - 3초 소요