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
출처 : https://gksdudrb922.tistory.com/m/154
'Back-End > Spring' 카테고리의 다른 글
[Spring] 복잡한 json 데이터 받기 (0) | 2023.02.10 |
---|---|
[Spring MVC] 서블릿 - HttpServletResponse (0) | 2023.02.09 |
[Spring] 서버에서 다른 서버의 Rest API 요청 및 응답 받기 (0) | 2023.02.09 |
[Spring Boot] Spring Boot + Jasper + MySQL 데이터베이스 예제 (0) | 2023.02.09 |
[Spring Boot] Spring Boot + Jasper Report Example (0) | 2023.02.09 |