개요
springboot 에서 jdbcTemplate 을 쓰면서, insert/upsert/update 수행 시 어느정도 성능차이가 발생하는지 궁금했다. 사실 성능의 순서는 insert -> upsert -> update 로 예상은 되지만 그래도 데이터 사이즈에 따라서 어느정도 될 지 간단히 비교해봤다.
환경은 로컬환경에서 mysql 이미지의 컨테이너를 올려서, 테스트코드로 롤백을 수행하지 않은채 진행했다.
- mysql 8.0
- mac m1
- spring-boot-starter-data-jdbc 2.6.11
코드
person 테이블에 name, email 은 unique key 로 잡혀있는 상태이다. duplicate key 발생 시, 컬럼을 업데이트 칠 수 있기 때문.
@Repository
class PersonRepository(
private val jdbcTemplate: JdbcTemplate
) {
/**
* insert
*/
fun insertPersonBulk(persons: List<Person>) {
val query = """
INSERT INTO person
(`name`, `email`, `remark`,`created_date`, `created_time`)
VALUES
(?, ?, ?, ?, ?)
""".trimIndent()
persons.chunked(BATCH_SIZE).forEach { chunkPersons ->
jdbcTemplate.batchUpdate(query, object: BatchPreparedStatementSetter {
override fun setValues(ps: PreparedStatement, i: Int) {
val person = chunkPersons[i]
ps.setString(1, person.name)
ps.setString(2, person.email)
ps.setString(3, person.remark)
ps.setDate(4, java.sql.Date.valueOf(LocalDate.now()))
ps.setTime(5, java.sql.Time.valueOf(LocalTime.now()))
}
override fun getBatchSize(): Int {
return chunkPersons.size
}
})
}
}
/**
* key duplicate -> update or insert
*/
fun upsertPersonBulk(persons: List<Person>) {
val query = """
INSERT INTO person
(`name`, `email`, `remark`,`created_date`, `created_time`)
VALUES
(?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE remark = ?
""".trimIndent()
persons.chunked(BATCH_SIZE).forEach { chunkPersons ->
jdbcTemplate.batchUpdate(query, object: BatchPreparedStatementSetter {
override fun setValues(ps: PreparedStatement, i: Int) {
val person = chunkPersons[i]
ps.setString(1, person.name)
ps.setString(2, person.email)
ps.setString(3, person.remark)
ps.setDate(4, java.sql.Date.valueOf(LocalDate.now()))
ps.setTime(5, java.sql.Time.valueOf(LocalTime.now()))
// key 충돌 시, 입력되는 리마크
ps.setString(6, person.remark)
}
override fun getBatchSize(): Int {
return chunkPersons.size
}
})
}
}
/**
* update
*/
fun updatePersonBulk(persons: List<Person>) {
val query = """
UPDATE
person
SET
remark = ?, created_date = ?, created_time = ?
WHERE
name = ? AND email = ?
""".trimIndent()
persons.chunked(BATCH_SIZE).forEach { chunkPersons ->
jdbcTemplate.batchUpdate(query, object: BatchPreparedStatementSetter {
override fun setValues(ps: PreparedStatement, i: Int) {
val person = chunkPersons[i]
// update 되는 값
ps.setString(1, person.remark)
ps.setDate(2, java.sql.Date.valueOf(LocalDate.now()))
ps.setTime(3, java.sql.Time.valueOf(LocalTime.now()))
ps.setString(4, person.name)
ps.setString(5, person.email)
}
override fun getBatchSize(): Int {
return chunkPersons.size
}
})
}
}
fun deleteAll(): Boolean {
val query = """
DELETE FROM person
""".trimIndent()
return jdbcTemplate.update(query) >= 1
}
companion object {
private const val BATCH_SIZE = 3000
}
}
결과
5000건
- insert : 4초
- upsert : 8초
- update : 12초
100000건
- insert : 92초
- upsert : 205초
- update : 297초
결과는 실제 서버가 돌아가는 환경에 따라 달라질 수 있기에, 참고로만 보면 좋을 것 같다.
'Spring' 카테고리의 다른 글
2022-10-30 [mongo] : mongoTemplate 의 bulkOperation 사용 (0) | 2022.10.30 |
---|---|
2022-03-27 [spring-cloud] : @RefreshScope (0) | 2022.03.27 |
2021-11-25 [test] : springboot 에서 테스트를 작성하면서, 내가 간과한 부분. 그리고 좀 더 생각해보기 (open-session-in-view) (0) | 2021.11.25 |
20201121 [java] proxy 에 대한 이해 (수정 : 2021-11-26) (0) | 2020.11.22 |
20111113 [transcation] 스프링 선언적 트랜잭션 (0) | 2020.11.20 |