데이터의 Total 개수와 해당 페이지에 해당하는 요소를 데이터베이스로부터 추출해 응답해야 하는 API를 개발해야 했다.
보통은 이런 것을 JPA의 페이징을 이용해서 만든다.
근데, 이것을 JDBC로 만들어보고 JPA보다 빠른지 궁금해져서 만들며 비교를 해보려고 한다.
각각 파이썬으로 데이터를 1000개 정도 넣고 테스트 해보았다.
- JDBC
우선 JDBC이다.
사용한 코드는 아래와 같다.
총 개수와 해당 요소들을 가져오는 쿼리문을 2번 날리게 된다.
Service
override fun professorGetWritingList(
assignmentId: Int,
page: Int,
pageSize: Int
): ResponseEntity<CoBoResponseDto<ProfessorGetWritingListRes>> {
val startTime = System.currentTimeMillis()
val totalElements = writingRepository.countByAssignmentIdWithJDBC(assignmentId)
val writings =
writingRepository.findByAssignmentIdOrderByStatePagingWithJDBC(
assignmentId = assignmentId,
page = page,
pageSize = pageSize
).map{
ProfessorGetWritingLisElementRes(
studentId = it.studentId,
createdAt = it.createdAt!!,
updatedAt = it.updatedAt!!,
writingState = it.state.value
)
}
println("TIME: ${System.currentTimeMillis() - startTime}ms")
return CoBoResponse(
data = ProfessorGetWritingListRes(
totalElements = totalElements,
writings = writings
),
coBoResponseStatus = CoBoResponseStatus.SUCCESS
).getResponseEntity()
}
Repository
override fun findByAssignmentIdOrderByStatePagingWithJDBC(assignmentId: Int, page: Int, pageSize: Int): List<Writing> {
val sql = "SELECT writing.id, writing.student_id, writing.assignment_id, writing.content, writing.state, writing.created_at, writing.updated_at, writing.submitted_at " +
"FROM writing WHERE writing.assignment_id = ? ORDER BY writing.state LIMIT ?, ?"
return jdbcTemplate.query(
sql, {rs, _ -> writingRowMapper(rs)}, assignmentId, page, pageSize
)
}
override fun countByAssignmentIdWithJDBC(assignmentId: Int): Long {
return jdbcTemplate.queryForObject("SELECT count(*) FROM writing WHERE assignment_id = ?", Long::class.java, assignmentId)!!
}
private fun writingRowMapper(resultSet: ResultSet): Writing {
return Writing(
id = resultSet.getInt("id"),
studentId = resultSet.getString("student_id"),
assignment = Assignment(resultSet.getInt("assignment_id")),
content = resultSet.getString("content"),
state = WritingStateEnum.from(resultSet.getShort("state"))!!,
createdAt = resultSet.getTimestamp("created_at").toLocalDateTime(),
updatedAt = resultSet.getTimestamp("updated_at").toLocalDateTime(),
submittedAt = resultSet.getTimestamp("submitted_at").toLocalDateTime()
)
}
0페이지에 요소를 20개로 속도를 측정해보면
요소 20개 | 요소 100개 |
39MS | 47MS |
41MS | 46MS |
44MS | 48MS |
38MS | 40MS |
이 정도의 속도가 나왔다, 역시 요소가 많아질수록 시간이 오래 걸리기는 한다.
- JPA
이번에는 간편한 JPA이다.
간단하게 PageRequest로 페이징해서 거기에서 totalElements와 요소들을 가져왔다.
override fun professorGetWritingList(
assignmentId: Int,
page: Int,
pageSize: Int
): ResponseEntity<CoBoResponseDto<ProfessorGetWritingListRes>> {
val startTime = System.currentTimeMillis()
val jpaList = writingRepository.findByAssignment(Assignment(assignmentId), PageRequest.of(page, pageSize))
val totalElements = jpaList.totalElements
val writings = jpaList.content.map {
ProfessorGetWritingListElementRes(
it.studentId, it.createdAt!!, it.updatedAt!!, it.state.value
)
}
println("TIME: ${System.currentTimeMillis() - startTime}ms")
return CoBoResponse(
data = ProfessorGetWritingListRes(
totalElements = totalElements,
writings = writings
),
coBoResponseStatus = CoBoResponseStatus.SUCCESS
).getResponseEntity()
}
전과 동일하게 측정해보자
요소 20개 | 요소 100개 |
128MS | 81MS |
63MS | 76MS |
61MS | 68MS |
61MS | 69MS |
확실히 JDBC로 호출하는 것이 빠른 것을 볼 수 있다.
사용자가 얼마 없으면 JPA로 개발하고, 사용자가 많아지면 JDBC로 개발할 것 같다.
이번은 이미 JDBC 코드로 만들어놔서 JPA를 사용하지는 않을 것 같다.
- JDBC 비동기
마지막으로 JDBC에서 호출하던 2개의 쿼리문을 비동기로 동시에 호출해보자.
JDBC 서비스 코드에서 아래같이 수정했다.
override fun professorGetWritingList(
assignmentId: Int,
page: Int,
pageSize: Int
): ResponseEntity<CoBoResponseDto<ProfessorGetWritingListRes>> {
val startTime = System.currentTimeMillis()
val totalElementsCompletableFuture = CompletableFuture.supplyAsync {
writingRepository.countByAssignmentIdWithJDBC(assignmentId)
}
val writingsCompletableFuture = CompletableFuture.supplyAsync {
writingRepository.findByAssignmentIdOrderByStatePagingWithJDBC(
assignmentId = assignmentId,
page = page,
pageSize = pageSize
).map{
ProfessorGetWritingListElementRes(
studentId = it.studentId,
createdAt = it.createdAt!!,
updatedAt = it.updatedAt!!,
writingState = it.state.value
)
}
}
val coboResponse = CoBoResponse(
data = ProfessorGetWritingListRes(
totalElements = totalElementsCompletableFuture.get(),
writings = writingsCompletableFuture.get()
),
coBoResponseStatus = CoBoResponseStatus.SUCCESS
)
println("TIME: ${System.currentTimeMillis() - startTime}ms")
return coboResponse.getResponseEntity()
}
실행해보면
요소 20개 | 요소 100개 |
35MS | 46MS |
37MS | 31MS |
29MS | 21MS |
30MS | 26MS |
이렇게 말도 안되는 속도가 나온다.
물론 가장 빠르기는 하지만, 그냥 JDBC를 사용했을 때와 비슷한 거 같기도....?
확실히 JPA가 편하고 비동기가 강력한 것 같다.
'크무톡톡 프로젝트' 카테고리의 다른 글
FastAPI VS WebFlux jemeter 성능 테스트 (0) | 2024.10.23 |
---|---|
Webflux + 코루틴으로 ChatGPT assistant 서버 구현하기 (4) | 2024.10.23 |
SpringBoot 시간 설정 (0) | 2024.08.09 |
JPA Soft Delete (0) | 2024.08.06 |
Docker를 활용한 Nginx로 Swagger proxy (0) | 2024.08.05 |