CompletableFuture로 코드 최적화하기

Gummybearr·2021년 6월 20일
0

삽질의 흔적

목록 보기
5/5
post-thumbnail

"쿼리가 느려요"를 조금 더 구체적인 표현으로 바꾸어보면 문제를 해결할 수 있을 때도 있다

문제

회사 코드를 짜던 중 데이터베이스를 자주 갔다오는 코드가 조금 답답하게 느껴졌다. 아무 생각 없이 옆에 계신 선배분께 "쿼리가 느린것 같아요"라고 말했더니 선배는 이를 조금 더 구체적인 표현으로 바꾸어 주셨다.

때로는 문제를 잘 정의하는 것이 문제 해결의 키가 될 때가 있다. 문제가 된 코드는 8번에 거쳐서 데이터베이스 쿼리를 날리고 있었는데, 이를 앞에서부터 하나씩 요청했기 때문에 네트워크 io를 기다리면서 낭비되는 시간이 많았던 것이었다.

이럴 때는 시간을 측정해보고 해결방법을 적용해 본 뒤에 다시 시간 측정을 해보고 비교해보는 것이 좋다

최적화 이전 코드 시간 측정

db쪽에서 캐시를 타지 않았을 때

1st
db query costed 310 ms
db query costed 122 ms
db query costed 87 ms
db query costed 106 ms
db query costed 194 ms
db query costed 411 ms
db query costed 139 ms
db query costed 47 ms
=> 1,416 ms

2nd
db query costed 292 ms
db query costed 184 ms
db query costed 135 ms
db query costed 155 ms
db query costed 223 ms
db query costed 445 ms
db query costed 142 ms
db query costed 42 ms
=> 1,618 ms

3rd
db query costed 389 ms
db query costed 160 ms
db query costed 108 ms
db query costed 161 ms
db query costed 267 ms
db query costed 557 ms
db query costed 2399 ms
db query costed 51 ms
=> 4,092 ms

평균 실행시간 = 2,375 ms

db쪽에서 캐시를 탔을 때

1st
db query costed 138 ms
db query costed 448 ms
db query costed 100 ms
db query costed 165 ms
db query costed 162 ms
db query costed 36 ms
db query costed 142 ms
db query costed 44 ms
=> 1,235 ms

2nd
db query costed 119 ms
db query costed 161 ms
db query costed 117 ms
db query costed 169 ms
db query costed 162 ms
db query costed 45 ms
db query costed 133 ms
db query costed 45 ms
=> 951 ms

3rd
db query costed 105 ms
db query costed 155 ms
db query costed 102 ms
db query costed 182 ms
db query costed 150 ms
db query costed 30 ms
db query costed 138 ms
db query costed 33 ms
=> 895 ms

4th
db query costed 96 ms
db query costed 111 ms
db query costed 105 ms
db query costed 168 ms
db query costed 162 ms
db query costed 55 ms
db query costed 107 ms
db query costed 28 ms
=> 832 ms

5th
db query costed 111 ms
db query costed 138 ms
db query costed 100 ms
db query costed 149 ms
db query costed 122 ms
db query costed 30 ms
db query costed 156 ms
db query costed 64 ms
=> 870 ms

평균 실행 시간 = 956 ms

해결방법

쿼리 요청을 병렬화하는 것으로 이를 해결할 수 있다고 생각했는데, 첫번째 쿼리의 결과를 활용하여 2~8번째 쿼리를 날리고 있기 때문에 첫번째 쿼리는 그대로 두고 2~8번째 쿼리를 병렬로 보내는 것이 좋다고 판단했다.

코틀린의 코루틴으로 최적화를 할까를 고민했는데, async await로 처리하는 것 보다는 completableFuture를 이용하는 것이 조금 더 코드가 보기에 좋다고 생각해서 completableFuture를 사용했다.

val 변수 = 쿼리?: 에러 던짐

lateinit var 변수: 타입
val 변수future = CompletableFuture.supplyAsync {
	adUnit = 쿼리?: 에러 던짐
}

...변수& 변수future선언 반복

val combinedFuture = CompletableFuture.allOf(모든 퓨쳐)
combinedFuture.get()

return 결과

최적화 이후 코드 시간 측정

db쪽에서 캐시를 타지 않았을 때

1st
db query costed 337 ms
병렬 query costed 2263 ms
=> 2,600 ms

2nd
db query costed 443 ms
병렬 query costed 422 ms
=> 865 ms

3rd
db query costed 365 ms
병렬 query costed 515 ms
=> 880 ms

평균 실행 시간 = 1, 448 ms

db쪽에서 캐시를 탔을 때

1st
db query costed 119 ms
병렬 query costed 199 ms
=> 318 ms

2nd
db query costed 94 ms
병렬 query costed 183 ms
=> 277 ms

3rd
db query costed 101 ms
병렬 query costed 181 ms
=> 282 ms

4th
db query costed 100 ms
병렬 query costed 179 ms
=> 279 ms

5th
db query costed 101 ms
병렬 query costed 172 ms
=> 273 ms

평균 실행 시간 = 285 ms

정리

db쪽에서 cache hit가 발생하지 않았을 경우에는 평균 2,375 ms에서 1,448 ms로 약 40% 성능 향상이 있었고
db쪽에서 cache hit가 발생했을 경우에는 평균 956 ms에서 285 ms로 약 70% 성능 향상이 있었다

profile
버그가 아니고 기능입니다만

0개의 댓글

관련 채용 정보