티스토리 뷰

데이터 베이스 접근을 모니터링을 해보기

모니터링 툴로는 Prometheus - Grafana으로 작동하였습니다.

 

1) 슬로우 쿼리(조회)로 인한 현상을 확인해보기

SELECT *
  FROM shop_order
 WHERE seq_shop_order = 1
   AND SLEEP(10) = 0
   
// QueryDsl 

queryFactory
    .select(Projections.constructor(OrderDetail.class,
            orderEntity.orderName,
            orderEntity.price,
            orderEntity.regId))
    .from(orderEntity)
    .where(orderEntity.id.eq(seqOrderId)
    .and(Expressions.booleanTemplate("function('SLEEP', 10) = 0")))
    .fetchOne();

 

위와 같은 쿼리를 작동시키면 조회당 10초가 걸리는 쿼리를 발생시킬수 있습니다.

 

서버 스펙은 도커를 통해 다음과 같이 조정하였습니다.

CPU: 2 Core
Memory: 1G

 

DB커넥션은 다음과 같이 설정하였습니다.

minimum-idle: 5
maximum-pool-size: 10

 

 

측정

종목            스레드 수 Idle 5 10 30 100
CPU 사용률 0.5% 2.5% 6.6% 4.4% 9.1%
Thread 수 24 (Live) 24 25 45 115
Connection 수 5 (Idle) 10 10(Active) 10 (Active)
20 (Pending)
10 (Active)
90 (Pending)
TPS   0.461/sec 0.913/sec 0.985/sec 1.4/sec (Error: 30%)

 

 

- 쓰레드 수

100개까지 늘었음에도, CPU 사용률은 크게 늘지 않았습니다.

 

- Heap 메모리

GC로 인해 관측이 되지 않았고, Pause Time은 조금씩 늘었습니다. (G1 GC 사용)

 

- Connection 수

Max Pool Size까지 큰 후에는 나머지 Connection은 보류(Pending)으로 인해 처리되지 않았습니다.

10 - 30 - 100 순의 Connection

 

- TPS

늘지 않을거라 예측하였는데, 미세하게나마 조금씩 증가합니다.

100건에서는 커넥션 타임아웃(60초)으로 인해 나머지 30%는 에러처리되었습니다. (이에 따라 응답속도 빨라짐) 

값은 예측치 10초당 10건처리 = 1 TPS 만큼 나오게되었습니다.

 

 


 

애플리케이션 내에서 스레드 수를 많이 생성할 수 있어도 쿼리 처리속도 / Connection 수 만큼의 처리량을

넘어서기 어렵다는 것을 알 수 있습니다.

 

어떻게 조치할 수 있을까요??

- Max Pool Size 조정

Connection수를 늘려 Pending상태를 줄이면 보다 처리량을 늘릴 수 있습니다.

다만, 커넥션 수를 많이 늘리게되면 DB자원을 그만큼 사용하게 되므로 DBA와의 소통이 필요합니다.

 

- Query 튜닝

쿼리 처리속도를 줄이면 줄일수록 커넥션 수당 처리량이 늘어날 수 있습니다.

가령 현재 쿼리가 10초인데, 1초로 줄이게된다면 1/sec * 10 Connection 으로 10 TPS를 처리할 수 있게됩니다.

 

 

 

 

그러면 DB자원이 무한해서 Connection 수를 계속 늘리면 TPS가 늘어날 수 있을까요?? 

 


 

2) 서버 스펙보다 과도한 요청 넣기

 

이번에는 Connection Pool Size를 키워보도록 하겠습니다.

Spring Boot의 tomcat스레드 수와

MySQL의 Max Connection도 올려주셔야합니다.

server:
  tomcat:
    threads:
      max: 500
      min-spare: 30

minimum-idle: 20
maximum-pool-size: 500

 

500까지 될지는 모르겠지만 엄청 큰 크기를 설정으로 주었습니다.

쓰레드 수 5까지는 의미가 없다고 생각하여 10부터 측정하였습니다.

 

종목                         스레드 수 30 100 300 500
CPU 사용률 10.6% 17.6% 27% 29.5%
Thread 수 45 115 315 515
Connection 수 30 (Active)
0 (Pending)
100 (Active)
0 (Pending)
300 (Active)
0 (Pending)
495 (Active)
5 (Idle)
TPS 2.7/sec 7.6/sec 23.0/sec 25.0/sec

 

워밍업작업(스레드수 500, CPU 사용률 80%) 으로 한번 실행 후 위와 같은 결과를 얻을 수 있었습니다.

생각외로 커넥션수에 비례해 TPS를 얻을 수 있었고, 워밍업을 한 후에 CPU 사용률은 적은 것을 확인할 수 있었습니다.

(아무래도 I/O 바운드 작업이다보니 그런것으로 생각합니다.)

 

 

다만, 30 -> 100 -> 300으로 갈때에 비해 300 -> 500으로 갈때 차이가 미미했습니다.

그러면 CPU Core 수를 한번 늘려보는 것은 어떨까요??

CPU Core 수를 4개 (2배) 로 수정하고 작동시켜보았습니다.

 

종목                         스레드 수 30 100 300 500
CPU 사용률 1.3% 6.1% 12.6% 14.5%
Thread 수 45 115 315 515
Connection 수 30 (Active)
0 (Pending)
100 (Active)
0 (Pending)
300 (Active)
0 (Pending)
495 (Active)
5 (Idle)
TPS 2.7/sec 9.1/sec 27.5/sec 44.3/sec

 

500스레드 수를 감당가능한 CPU 코어수가 있기에 TPS가 이전보다 향상된것을 확인할 수 있었습니다.

 

 

여기서 하나더! Tomcat 스레드 수를 200으로 다시 낮춰본다면 어떻게 변할까요? 

아쉽게도 TPS(16.5/sec) 는 떨어지게 되는데, 이는 DB에 접근하는 동안 스레드들이 200개까지 생성 후

쿼리가 실행되는동안 time-wating을 하고 있기 때문입니다.

(이것에 대해서는 버츄얼 쓰레드로 변경해서 측정해볼 생각입니다.)

Connection = 500, Thread = 200 처리시

 

 


 

결론은 서버의 Spec& Thread 수, Connection 수 가 데이터베이스 접근 작업에 영향을 끼치는 것을 알 수 가 있습니다.

그러나 위의 결과대로 Thread개수만큼 Connection을 만드는 것은 잘못된 생각일 수 있습니다.

 

위의 경우 매우 긴 트랜잭션이 존재할때의 측정치이며, 짧은 트랜잭션이 다수 존재하는 애플리케이션이면

각 애플리케이션이 가져야할 Connection 수가 저렇게까지 필요하지 않습니다.

 

DB Connection은 애플리케이션과 달리 한정적인 자원 (물론 레플리카 구조로 조금 여유 가능하지만)

이기때문에 애플리케이션 성능이 너무 뛰어나서 Thread수를 엄청 늘려도 DB서버 스펙에서 받는 한계가 있습니다.

 

 


 

3) 쿼리 튜닝 가정하기

이번에는 쿼리를 튜닝했다 가정하고 쿼리속도를 80ms정도로 낮춰보도록 하겠습니다.

CPU: 2코어

Connection: 5(min) - 10(Max) 로 수정하고 진행하였습니다. 

 

종목                         스레드 수 30 100 300 500
CPU 사용률 1.3% 6.1% 22.3% 28.2%
Thread 수 45 45 315 215
TPS 8.6/sec 89.8/sec 96.1/sec 96.4/sec


TPS 90을 지향한다면 적합한 스펙이라고 판단할 수 있겠습니다.

(톰캣 스레드 max를 500으로 변경했을때 5TPS 정도의 향상이 있었습니다.)

 

connection 수를 10 - 20 으로 수정해보겠습니다.

300, 500의 결과만 보도록 하겠습니다.

종목                         스레드 수 300 500
CPU 사용률 22.3% 28.2%
Thread 수 215 215
TPS 188.6/sec 189.0/sec

 

TPS 190을 지향하는 스펙이라고 볼 수 있겠습니다.

(톰캣 스레드 max를 500으로 변경했을때 성능의 큰 차이가 없었습니다.)

 

 

 


 

사실 TPS가 높으면 높을수록 좋은것은 맞습니다.

하지만, 애플리케이션 목표 TPS에 따라 커넥션의 수를 가져가는게 올바르다고 생각합니다.

서버유저가 1명인데 10000TPS가 나온다한들 의미가 없겠죠?

 

또한, DB 커넥션 수는 제한적인데, 애플리케이션 서버들이 생성될 수 록 한정된 자원을 가져가기 때문입니다.

-> 커넥션 수를 크게 맞춰가는거보다 어떻게 적게 분배할 수 있을까 하는 방향으로 가는게 적합하지 싶습니다.

 

HikariCP에서 추천하는 풀 사이징 처리방법이 있는데요. (난해한 경우)

 

- 장기 트랜잭션과, 단기 트랜잭션을 혼합하는 애플리케이션의 경우

2개의 커넥션 풀을 사용하는 방법

 

- 장기 트랜잭션이 일정한 단위 배치 작업으로 실행되는 애플리케이션의 경우

작업 단위에 커넥션 수를 맞추지 않고 커넥션 수에 작업단위를 맞추는 방법

 

- 풀 잠금에서 교착상태를 피하는 최소 풀사이즈 계산

pool size = Tn x (Cm - 1) + 1

여기서 Tn은 최대 스레드 수이고 Cm은 단일 스레드가 보유할 수 있는 최대 동시 연결 수(Connection 수)입니다.

 

- 잘 모르는 경우? 

Core Count * 2 + n 로 설정한후 모니터링을 통해 개선해나가는 방법

 


 

개발을 하면서 궁금했던 사항들을 직접 테스트 해볼 수 있었던 시간이었습니다.

읽어주셔서 감사합니다.

 

 

참고문서

https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing

 

테스트 환경 구성

th1993kim/Study: 공부한것들 모아두기 - taehyun-shop-multi-project 디렉토리

'SPRING 공부 > 기타' 카테고리의 다른 글

MyBatis 의도치 않은 캐싱  (0) 2023.09.05
아파치 카프카 문서보고 공부하기 #2  (0) 2023.01.04
아파치 카프카 문서보고 공부하기 #1  (0) 2022.12.26
RFC 문서  (0) 2022.12.03
템플릿 엔진  (0) 2022.02.01
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함