우노
[Kotlin] Bulk Insert 본문
Bulk Insert란?
- Bulk Insert는 데이터베이스에 여러 개의 데이터를 한 번에 삽입하는 기법으로,
- 여러 번의 Single Insert 대신 한 번에 여러 레코드를 삽입하여 속도와 효율성을 개선할 수 있습니다.
Single Insert vs Bulk Insert
Single Insert
데이터를 하나씩 삽입하는 방식입니다.
INSERT INTO table1 (col1, col2) VALUES (val11, val12);
여러 번 반복되는 경우
INSERT INTO table1 (col1, col2) VALUES (val11, val12); INSERT INTO table1 (col1, col2) VALUES (val21, val22); INSERT INTO table1 (col1, col2) VALUES (val31, val32);
Bulk Insert
여러 개의 데이터를 한 번에 삽입하는 방식입니다.
INSERT INTO table1 (col1, col2) VALUES (val11, val12), (val21, val22), (val31, val32);
Bulk Insert의 장점
- 속도 개선: 한 번의 네트워크 호출로 여러 개의 데이터를 삽입하여 성능이 향상됩니다.
- 리소스 절약: 네트워크 호출 횟수와 데이터베이스 연결을 줄여 시스템 자원을 절약할 수 있습니다.
- 트랜잭션 관리: 여러 데이터를 하나의 트랜잭션으로 처리하여 일관성을 유지하고, 오류 발생 시 전체 작업을 롤백할 수 있습니다.
JPA와 JDBC에서 Bulk Insert 처리 방법
JPA의 saveAll() 사용하기
JPA는 saveAll() 메서드를 통해 여러 데이터를 한 번에 저장할 수 있습니다.
하지만 내부적으로 반복적으로 save()를 호출하여 데이터를 개별적으로 처리하므로 진정한 Bulk Insert가 아님
- save() : N개의 트랜잭션과 N개의 쿼리
- saveAll() : 1개의 트랜잭션과 N개의 쿼리
문제점
- Hibernate의 쓰기 지연(Write-Behind) 전략
- 데이터는 영속성 컨텍스트에 저장되고 트랜잭션이 커밋될 때 INSERT 쿼리가 실행됩니다.
- 하지만 IDENTITY 전략을 사용하면, 각 엔티티의 ID를 알기 위해 매번 INSERT가 실행되어 Batch Insert가 불가능합니다.
- 영속성 컨텍스트의 메모리 문제
- 많은 데이터를 한 번에 저장하면 메모리 사용량이 증가하고 성능 저하가 발생합니다.
- 성능 문제
- JPA의 saveAll()은 개별 쿼리를 실행하므로 네트워크 호출이 많아지고 속도가 느려질 수 있습니다.
- Hibernate의 쓰기 지연(Write-Behind) 전략
해결 방법
ID 생성 전략 변경
- IDENTITY 대신 SEQUENCE 또는 TABLE 전략을 사용하여 ID 값을 미리 생성할 수 있습니다.
Batch Insert 활성화
Hibernate 설정을 통해 Batch Insert를 활성화합니다.
spring.jpa.properties.hibernate.jdbc.batch_size=30 spring.jpa.properties.hibernate.order_inserts=true spring.jpa.properties.hibernate.order_updates=true
JDBC의 batchUpdate 사용하기
JDBC는 데이터베이스에 최적화된 Batch Insert 기능을 제공합니다.
JdbcTemplate의 batchUpdate() 메서드를 사용하면 여러 데이터를 효율적으로 삽입할 수 있습니다.
String sql = "INSERT INTO table1 (col1, col2) VALUES (?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, dataList.get(i).getCol1()); ps.setString(2, dataList.get(i).getCol2()); } @Override public int getBatchSize() { return dataList.size(); } });
Native SQL을 사용한 Bulk Insert
Spring Data JPA는 Native SQL을 지원합니다.
이를 통해 직접 SQL 쿼리를 실행하여 효율적으로 Bulk Insert를 처리할 수 있습니다.
@Query(value = "INSERT INTO table1 (col1, col2) VALUES :values", nativeQuery = true) void bulkInsert(@Param("values") List<Object[]> values);
Bulk Insert의 성능 비교
방법 | 10000건 처리 시간 | 장점 | 단점 |
---|---|---|---|
JPA saveAll() | 느림 (~5초) | 코드 간결, 트랜잭션 관리 자동화 | 개별 INSERT 쿼리 실행으로 성능 저하 |
JDBC batchUpdate() | 빠름 (~1초) | 최적화된 성능, 네트워크 호출 감소 | 직접 쿼리 작성 필요 |
Native SQL | 빠름 (~1초) | 데이터베이스 기능 활용 가능 | 유지보수가 어렵고 JPA의 장점 활용 불가 |
Bulk Insert 시 주의사항
- MySQL의 max_allowed_packet 설정
- MySQL에서 한번에 보낼 수 있는 데이터 크기 제한이 max_allowed_packet 설정값을 넘지 않도록 주의해야 합니다.
- 너무 큰 데이터를 한 번에 보내지 않도록 배치 크기를 조절하는 것이 중요합니다.
- 메모리 관리
- 대량 데이터를 처리할 경우 메모리 부족 문제가 발생할 수 있습니다.
- 배치 크기를 조정하여 메모리 사용량을 관리하세요.
- 롤백 비용
- 배치 처리 중 오류가 발생하면 전체 트랜잭션이 롤백되므로, 배치 크기를 적절히 조정하여 롤백 비용을 최소화해야 합니다.
결론
- Bulk Insert는 대량 데이터를 효율적으로 삽입할 수 있는 기법으로, 성능 향상과 리소스 절약이 가능합니다.
- JPA를 사용할 때는 Hibernate Batch Insert 설정을 활용하거나, JDBC batchUpdate와 Native Query를 적절히 활용하여 성능을 최적화할 수 있습니다.
- 데이터 크기, 메모리 사용량, 롤백 비용을 고려하여 배치 크기와 전략을 신중히 설계하세요.
참고
'Web_App > Kotlin' 카테고리의 다른 글
[Kotlin] @Transactional, Service vs Repository 어디에 적용할까? (0) | 2024.12.26 |
---|---|
[Kotlin] @Transactional (0) | 2024.12.23 |
[Kotlin] @Service vs @Component (2) | 2024.12.23 |
[Kotlin] Controller와 Service의 계층 구조 (0) | 2024.12.20 |
[Kotlin] @SqlResultSetMapping (0) | 2024.12.20 |
Comments