danuri
오늘의 기록
danuri
전체 방문자
오늘
어제
  • 오늘의 기록 (307)
    • java (150)
      • java (33)
      • spring (63)
      • jpa (36)
      • querydsl (7)
      • intelliJ (9)
    • kotlin (8)
    • python (24)
      • python (10)
      • data analysis (13)
      • crawling (1)
    • ddd (2)
    • chatgpt (2)
    • algorithm (33)
      • theory (9)
      • problems (23)
    • http (8)
    • git (8)
    • database (5)
    • aws (12)
    • devops (10)
      • docker (6)
      • cicd (4)
    • book (44)
      • clean code (9)
      • 도메인 주도 개발 시작하기 (10)
      • 자바 최적화 (11)
      • 마이크로서비스 패턴 (0)
      • 스프링으로 시작하는 리액티브 프로그래밍 (14)
    • tistory (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

태그

  • docker
  • 도메인 주도 설계
  • Bitmask
  • Kotlin
  • Spring
  • S3
  • Java
  • DDD
  • AWS
  • PostgreSQL
  • Security
  • Thymeleaf
  • JPA
  • POSTGIS
  • ChatGPT
  • SWAGGER
  • mockito
  • RDS
  • 등가속도 운동
  • connection
  • Jackson
  • 마이크로서비스패턴
  • Saving Plans
  • gitlab
  • 자바 최적화
  • 트랜잭션
  • reactive
  • nuribank
  • Database
  • CICD

최근 댓글

최근 글

hELLO · Designed By 정상우.
danuri

오늘의 기록

java/spring

[Spring] Spring Jdbc - batchUpdate()를 사용한 bulk Insert 최적화

2021. 8. 6. 01:23

 

Spring Data JPA의 saveAll()메서드는 인자 값으로 받은 entity들을 저장하는 메서드이다.

실제로 실행해보면 여러 엔티티들에 대한 insert 쿼리가 각각 한 번 씩 차례대로 나가는 것을 볼 수 있다.

엔티티의 수가 적으면 상관없지만, 10,000건, 100,000건 처럼 많아지면 엔티티 1 : insert 1 방식은 부담이 될 수 있다.

 

인터넷을 검색해보면 hibernate.jdbc.batch_size: 50 처럼 한 번에 여러 건을 insert하는 벌크 연산이 많이 소개되어 있다.

이 방법을 사용해도 좋지만, 여기서는 Spring JPA가 아닌 JDBC를 사용한 보다 빠르고 강력한 연산인 batchUpdate()를 소개하고자 한다.

 

Bulk Insert

bulk insert는 여러 insert쿼리를 합쳐서 보낼 수 있는 방법이다.

3건의 데이터를 insert 한다고 하자.

INSERT INTO table1 (col1, col2) VALUES (val11, val12);
INSERT INTO table1 (col1, col2) VALUES (val21, val22);
INSERT INTO table1 (col1, col2) VALUES (val31, val32);

이렇게 하면 개별 insert이고,

 

INSERT INTO table1 (col1, col2) VALUES
(val11, val12),
(val21, val22),
(val31, val32);

이렇게 하면 batch insert다. batch insert가 개별 insert에 비해 훨씬 효율적임을 쉽게 알 수 있다.

 

SaveAll() - batchUpdate()

Spring Data JPA에 정의되어 있는 saveAll()메서드는 기본적으로 개별 insert를 제공한다.

 

그러나 대량의 엔티티를 insert해야 하는 경우, batch insert를 사용해야 한다.

새로운 리포지토리를 생성해 Jdbc를 사용해서 saveAll()메서드를 재정의해보자.

 

@Repository
@RequiredArgsConstructor
public class FoodJdbcRepository {

    private final JdbcTemplate jdbcTemplate;

    private int batchSize = 10000;

    public void saveAll(List<Food> items) {
        int batchCount = 0;
        List<Food> subItems = new ArrayList<>();
        for (int i = 0; i < items.size(); i++) {
            subItems.add(items.get(i));
            if ((i + 1) % batchSize == 0) {
                batchCount = batchInsert(batchCount, subItems);
            }
        }
        if (!subItems.isEmpty()) {
            batchCount = batchInsert(batchCount, subItems);
        }
        System.out.println("batchCount: " + batchCount);
    }

    private int batchInsert(int batchCount, List<Food> subItems) {
        jdbcTemplate.batchUpdate("INSERT INTO food (`food_id`, `name`) VALUES (?, ?)", new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        ps.setString(1, subItems.get(i).getId());
                        ps.setString(2, subItems.get(i).getName());
                    }
                    @Override
                    public int getBatchSize() {
                        return subItems.size();
                    }
                });
        subItems.clear();
        batchCount++;
        return batchCount;
    }
}

Food라는 객체를 Food 테이블에 batch insert로 저장한다고 했을 때의 예시이다.

 

batchSize 변수를 통해 배치 크기를 지정하고, 전체 데이터를 배치 크기로 나눠서 Batch Insert를 실행하고, 자투리 데이터를 다시 Batch Insert로 저장한다.

 

-> 실제로 Spring Data JPA와 비교했을 때, batchUpdate()가 압도적인 성능 우위를 보여준다.

 

+) 참고로 MySQL의 경우 datasource에 rewriteBatchedStatements=true 옵션을 추가해 주어야 한다.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
    username: admin
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver

 

'java > spring' 카테고리의 다른 글

[Spring] 개발, 운영 환경 별 profile 설정  (0) 2022.01.09
[Spring] FCM 서버 구축하기 (특정 시간대에 알림 보내기)  (4) 2021.12.10
[Spring] SpringBatch를 사용해 csv 파일 읽어 DB에 저장  (4) 2021.08.06
[Spring] AWS S3, csv 파일 읽어서 DB에 저장  (0) 2021.08.05
[Spring] AWS S3에서 Spring Boot로 파일 다운로드  (0) 2021.08.05
    'java/spring' 카테고리의 다른 글
    • [Spring] 개발, 운영 환경 별 profile 설정
    • [Spring] FCM 서버 구축하기 (특정 시간대에 알림 보내기)
    • [Spring] SpringBatch를 사용해 csv 파일 읽어 DB에 저장
    • [Spring] AWS S3, csv 파일 읽어서 DB에 저장
    danuri
    danuri
    IT 관련 정보(컴퓨터 지식, 개발)를 꾸준히 기록하는 블로그입니다.

    티스토리툴바