모종닷컴

Batch Update 3편 본문

Programming

Batch Update 3편

모종 2022. 5. 7. 02:51
반응형

지난글에서 쿼리를 수정함으로써 아주 굉장한 효과를 보았습니다. 하지만 만족하지 않습니다. 

불 - 만

고민 : 작업을 왜 하나의 스레드로만 하고 있었을까?

 지금까지 Batch Update의 1편, 2편 모두 잘 보았더라면 결국 저희가 하고 있는 코드는 id가 1 ~ 1,000,000 인 데이터 행들을 일괄적으로 업데이트하는겁니다. 그렇다면 이런 생각을 해볼 수 있습니다. 왜 내가 이걸 스레드 하나로 돌리고 있어야 할까?? 여러 개의 스레드를 이용해서 각각 범위를 나누어 실행시키면 더 빠르지 않을까 하고 말이죠.

어라?

예를 들면 1~ 10000 은 스레드1이 담당하고 10001 ~ 20000는 스레드2가 담당하도록 한다면 동일한 행을 참고할 일도 없으니 여러 개의 스레드로 나누어서 실행해도 문제없을 것 같습니다. 일단 해보도록 하죠! 

fun `스레드 두개로 테스트`() {
    val executors = Executors.newCachedThreadPool()
    val future1 = CompletableFuture.supplyAsync({ batchUpdate(1L, 500_000L) }, executors)
    val future2 = CompletableFuture.supplyAsync({ batchUpdate(500_000L, 1_000_000L) }, executors)
    CompletableFuture.allOf(future1, future2).join()
}

private fun batchUpdate(start: Long, end: Long) {
    val rowMapper = RowMapper { rs, _ ->
        LoginToken(
            id = rs.getLong("id"), // 8
            createdAt = LocalDateTime.parse(rs.getString("created_at"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) // 26
        )
    }
    val chunkSize = 1000
    (start until end).chunked(chunkSize) { subList ->
        val selectLoginToken = "select * from login_token where id between ${subList.first()} and ${subList.last() + 1}"
        val selected = jdbcTemplate.query(selectLoginToken, rowMapper).onEach {
            it.expiredDt = it.createdAt!!.plusDays(7).toLocalDate()
        }

        val buffer = StringBuffer()
        buffer.append("update login_token SET expired_dt = CASE\n")
        selected.forEach { buffer.append("when id = ${it.id} then '${it.expiredDt.toString()}'") }
        buffer.append("END WHERE id BETWEEN ${selected.first().id} and ${selected.last().id}")

        jdbcTemplate.update(buffer.toString())
    }
}

실행결과 20초가 소요되었습니다. 느낌상 스레드를 2개로 나누었으면 전보다 2배로 성능이 좋아져야 하는데 에잇.. 

3개로 늘려서 테스트해볼까요? 

15초로 줄어든 모습입니다. 이제는 정말 만족하고 사용해도 될 것 같은 느낌입니다!-! 

저는 이제 만족합니다 ㅎㅎ

드디어 길고 길었던 저의 고민들과 고민들에 대한 저의 해결법을 다 적어보았습니다. 최초 9분이 걸리던 작업을 15초로 줄이기는 했지만 아직도 분명 더 성능을 끌어올릴수 있는 방법이 있을것만 같습니다. 훗날 좀 더 좋은 방법이 생각난다면 4편으로 다시 이어보도록 하겠습니다.

지금까지 긴 글 읽어주셔서 감사합니다 :) 

반응형