Spring Data JPA의 saveAll은 인자 값으로 받은 entity들을 저장하는 메서드이다. 실제로 실행해보면 정상적으로 작동하는 것을 확인할 수 있지만, 로그를 보면 하나씩 insert 하는 것을 확인할 수 있다.
val items = (1..1000).map { Item(name = "피자", amount = 10000) }
val measureTimeMillis = measureTimeMillis{ itemRepository.saveAll(items) }
println("실행시간 $measureTimeMillis")
이를 해결하기 위한 방법들도 존재하지만, entity의 id값의 결정을 db에 위임하기 위해 @GeneratedValue(strategy = GenerationType.IDENTITY)
로 정의했다면 사용할 수 없다.
따라서 이러한 상황에서 매우 많은 양의 데이터를 한번에 저장할 때 saveAll을 사용하는 것은 시스템의 성능 저하를 야기할 수 있기 때문에 주의해야한다.
기존의 entity id 생성 전략을 바꾸는 것은 쉽게 결정할 일이 아니기 때문에 이러한 상황에서 batch insert를 수행하기 위해서 jdbcTemplate
의 batchUpdate
를 사용할 수 있다.
override fun batchInsert(items: List<Item>) {
jdbcTemplate.batchUpdate("insert into item (name, amount) values (?, ?)",
items,
100,
object : ParameterizedPreparedStatementSetter<Item?> {
override fun setValues(ps: PreparedStatement, argument: Item) {
ps.setString(1, argument.getName())
ps.setInt(2, argument.getAmount())
}
})
}
batchUpdate
메서드에서 첫 번째 인자로 query, 두 번째 인자로 저장할 데이터의 collection, 세 번째 인자로 batch 처리할 크기, 마지막으로 ParameterizedPreparedStatementSetter를 해주면 된다.
ParameterizedPreparedStatementSetter에서 db table에 저장 될 각 column의 내용을 지정해주면 된다.
fun batchInsert(items: List<Item>) {
jdbcTemplate.batchUpdate(
"insert into item (name, amount) values (?, ?)", items, 100
) { ps, argument ->
ps.setString(1, argument.getName())
ps.setInt(2, argument.getAmount())
}
}
lambda expression으로 나타내면 위와 같이 표현할 수도 있다.
여기서 주의할 점은
mysql jdbc의 경우 jdbc url에rewriteBatchedStatements=true
옵션을 추가한다.
해당 옵션을 추가하지 않으면 batch insert가 실행되지 않는다.
그리고 mysql에서 실제로 처리되는 쿼리를 확인하기 위해 profileSQL=true&logger=Slf4JLogger
추가한다.
실행결과를 보면 앞서 수행한 saveAll보다 빠른 219 ms에 실행된 것을 확인할 수 있으며, 실제 쿼리도 batch insert 형태로 처리한 것을 확인할 수 있다.
여기서 1000개의 entity를 저장하기 위해 100개씩 저장되는 batch insert가 10번 실행된다.
예제코드는 https://github.com/pss89513/batch-insert-example 에서 확인할 수 있다.
참고문서
- https://woowabros.github.io/experience/2020/09/23/hibernate-batch.html
- https://homoefficio.github.io/2020/01/25/Spring-Data%EC%97%90%EC%84%9C-Batch-Insert-%EC%B5%9C%EC%A0%81%ED%99%94/
- https://ohgym.tistory.com/48
- https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc-batch-multi