[Spring] ThreadPool & HikariCP 튜닝

개요

 

[Spring] nGrinder 부하 테스트 1) 쿼리 튜닝

개요 GitHub - sjiwon/study-with-me-be: 여기서 구해볼래? Backend Repository (Refactoring) 여기서 구해볼래? Backend Repository (Refactoring). Contribute to sjiwon/study-with-me-be development by creating an account on GitHub. github.com 본

sjiwon-dev.tistory.com

메인 페이지에 노출되는 스터디 조회 API와 관련된 3가지 쿼리에 대한 튜닝을 진행함으로써 기존에 구현한 로직과 비교해서 꽤 괜찮은 성능 향상을 이루어 냈다

  1. 등록날짜 기준
  2. 좋아요 개수 기준
  3. 리뷰 개수 기준

이번에는 ThreadPool & HikariCP 튜닝을 진행해서 최적의 값을 도출해보려고 한다

 

참고 포스팅

 

[Java] ThreadPool

ThreadPool 어떤 요청이 들어왔을 때 해당 요청을 처리하기 위해서 쓰레드를 사용하는 가장 Naive한 방법은 요청마다 쓰레드를 생성하고 할당하는 것이다 쓰레드가 필요한 시점에 생성 요청 OS가 해

sjiwon-dev.tistory.com

 

[Spring] Tomcat ThreadPool 1) Connector

개요 [Java] ThreadPool ThreadPool 어떤 요청이 들어왔을 때 해당 요청을 처리하기 위해서 쓰레드를 사용하는 가장 Naive한 방법은 요청마다 쓰레드를 생성하고 할당하는 것이다 쓰레드가 필요한 시점에

sjiwon-dev.tistory.com

 

[Spring] Tomcat ThreadPool 2) 주요 설정값 및 테스트

개요 [Spring] Tomcat ThreadPool 1) Connector 개요 [Java] ThreadPool ThreadPool 어떤 요청이 들어왔을 때 해당 요청을 처리하기 위해서 쓰레드를 사용하는 가장 Naive한 방법은 요청마다 쓰레드를 생성하고 할당

sjiwon-dev.tistory.com

 

[Spring] HikariCP 1) DBCP란?

DB Connection Web Application Server(WAS) ↔ DB Server간에 네트워크 통신을 위해서는 TCP/IP 기반으로 Connection을 맺어야 한다 WAS에서는 적절한 DB Driver를 통해서 DBMS에 Connection 요청을 진행한다 DB Driver는 WAS

sjiwon-dev.tistory.com

 

[Spring] HikariCP 2) Connection을 다루는 메커니즘

개요 [Spring] HikariCP 1) DBCP란? DB Connection Web Application Server(WAS) ↔ DB Server간에 네트워크 통신을 위해서는 TCP/IP 기반으로 Connection을 맺어야 한다 WAS에서는 적절한 DB Driver를 통해서 DBMS에 Connection 요

sjiwon-dev.tistory.com

 

 

튜닝 진행

  • Tomcat의 maxConnections & acceptCount는 기본값으로 고정 (8192 & 100)
  • Tomcat의 maxThreads & HikariCP의 maximumPoolSize의 케이스별 부하 테스트 진행

 

1) maxThreads 50 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 222.0 (max 268.5)
Response Time = 452.80
Executed Test = 12489
Errors = 1
TPS = 230.3 (max 261.5)
Response Time = 440.54
Executed Test = 12961
Errors = 0
TPS = 260.3 (max 315.5)
Response Time = 387.89
Executed Test = 14645
Errors = 0
maximumPoolSize 10 TPS = 237.8 (max 276.0)
Response Time = 423.51
Executed Test = 13382
Errors = 1
TPS = 239.8 (max 274.5)
Response Time = 416.99
Executed Test = 13488
Errors = 1
TPS = 270.5 (max 327.5)
Response Time = 370.75
Executed Test = 15207
Errors = 1
maximumPoolSize 15 TPS = 219.0 (max 266.0)
Response Time = 458.61
Executed Test = 12324
Errors = 0
TPS = 247.6 (max 323.5)
Response Time = 409.22
Executed Test = 13933
Errors = 0
TPS = 253.8 (max 292.0)
Response Time = 395.06
Executed Test = 14281
Errors = 0
maximumPoolSize 20 TPS = 224.9 (max 265.5)
Response Time = 447.61
Executed Test = 12652
Errors = 1
TPS = 230.8 (max 278.0)
Response Time = 437.70
Executed Test = 12988
Errors = 0
TPS = 278.3 (max 394.5)
Response Time = 362.84
Executed Test = 15661
Errors = 0
maximumPoolSize 25 TPS = 224.0 (max 278.0)
Response Time = 450.40
Executed Test = 12603
Errors = 1
TPS = 228.1 (max 274.0)
Response Time = 441.34
Executed Test = 12826
Errors = 1
TPS = 265.3 (max 328.0)
Response Time = 377.95
Executed Test = 14912
Errors = 1

maxThreads 50.zip
2.86MB

 

2) maxThreads 100 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 231.6 (max 272.5)
Response Time = 440.23
Executed Test = 13031
Errors = 0
TPS = 266.3 (max 321.0)
Response Time = 376.17
Executed Test = 14970
Errors = 1
TPS = 288.4 (max 342.5)
Response Time = 349.64
Executed Test = 16225
Errors = 1
maximumPoolSize 10 TPS = 231.7 (max 271.5)
Response Time = 435.68
Executed Test = 13036
Errors = 1
TPS = 241.0 (max 283.5)
Response Time = 418.60
Executed Test = 13561
Errors = 1
TPS = 269.5 (max 313.5)
Response Time = 374.64
Executed Test = 15161
Errors = 1
maximumPoolSize 15 TPS = 247.0 (max 297.0)
Response Time = 406.52
Executed Test = 13892
Errors = 0
TPS = 240.7 (max 288.5)
Response Time = 420.21
Executed Test = 13549
Errors = 0
TPS = 277.0 (max 338.0)
Response Time = 364.27
Executed Test = 15577
Errors = 1
maximumPoolSize 20 TPS = 216.1 (max 278.0)
Response Time = 467.92
Executed Test = 12155
Errors = 0
TPS = 239.5 (max 292.5)
Response Time = 418.64
Executed Test = 13470
Errors = 0
TPS = 266.7 (max 317.5)
Response Time = 378.94
Executed Test = 15008
Errors = 0
maximumPoolSize 25 TPS = 196.4 (max 249.0)
Response Time = 512.43
Executed Test = 11051
Errors = 0
TPS = 234.6 (max 278.5)
Response Time = 428.02
Executed Test = 13189
Errors = 0
TPS = 254.8 (max 298.0)
Response Time = 396.74
Executed Test = 14338
Errors = 0

maxThreads 100.zip
2.90MB

 

3) maxThreads 150 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 237.7 (max 276.0)
Response Time =  423.12
Executed Test = 13367
Errors = 1
TPS = 246.2 (max 301.5)
Response Time = 409.17
Executed Test = 13848
Errors = 1
TPS = 299.9 (max 349.5)
Response Time = 336.64
Executed Test = 16868
Errors = 1
maximumPoolSize 10 TPS = 218.6 (max 248.5)
Response Time = 463.02
Executed Test = 12292
Errors = 0
TPS = 232.6 (max 265.0)
Response Time = 423.96
Executed Test = 13094
Errors = 1
TPS = 271.5 (max 320.5)
Response Time = 371.76
Executed Test = 15280
Errors = 1
maximumPoolSize 15 TPS = 223.4 (max 287.0)
Response Time = 453.00
Executed Test = 12570
Errors = 0
TPS = 247.7 (max 296.0)
Response Time = 404.27
Executed Test = 13929
Errors = 0
TPS = 267.2 (max 343.5)
Response Time = 375.18
Executed Test = 15037
Errors = 0
maximumPoolSize 20 TPS = 213.1 (max 249.5)
Response Time = 469.93
Executed Test = 11980
Errors = 1
TPS = 247.4 (max 312.0)
Response Time = 406.60
Executed Test = 13915
Errors = 1
TPS = 258.9 (max 299.0)
Response Time = 388.49
Executed Test = 14571
Errors = 0
maximumPoolSize 25 TPS = 219.5 (max 295.5)
Response Time = 459.09
Executed Test = 12356
Errors = 1
TPS = 239.8 (max 270.0)
Response Time = 419.79
Executed Test = 13485
Errors = 1
TPS = 263.2 (max 297.5)
Response Time = 382.44
Executed Test = 14817
Errors = 1

maxThreads 150.zip
2.89MB

 

4) maxThreads 200 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 240.4 (max 322.5)
Response Time = 418.80
Executed Test = 13529
Errors = 0
TPS = 239.4 (max 293.0)
Response Time = 419.76
Executed Test = 13454
Errors = 0
TPS = 291.8 (max 335.0)
Response Time = 347.42
Executed Test = 16418
Errors = 0
maximumPoolSize 10 TPS = 243.8 (max 282.0)
Response Time = 410.73
Executed Test = 13709
Errors = 1
TPS = 251.0 (max 292.0)
Response Time = 402.37
Executed Test = 14122
Errors = 1
TPS = 277.2 (max 345.5)
Response Time = 363.70
Executed Test = 15598
Errors = 1
maximumPoolSize 15 TPS = 228.0 (max 271.0)
Response Time = 443.59
Executed Test = 12829
Errors = 0
TPS = 231.3 (max 281.0)
Response Time = 436.02
Executed Test = 13019
Errors = 0
TPS = 259.5 (max 307.0)
Response Time = 386.16
Executed Test = 14597
Errors = 1
maximumPoolSize 20 TPS = 228.9 (max 286.5)
Response Time = 438.13
Executed Test = 12873
Errors = 0
TPS = 226.5 (max 272.0)
Response Time = 446.23
Executed Test = 12732
Errors = 0
TPS = 255.8 (max 316.0)
Response Time = 393.36
Executed Test = 14374
Errors = 0
maximumPoolSize 25 TPS = 205.8 (max 243.0)
Response Time = 491.12
Executed Test = 11586
Errors = 0
TPS = 223.5 (max 267.5)
Response Time = 452.20
Executed Test = 12280
Errors = 0
TPS = 251.2 (max 328.5)
Response Time = 399.46
Executed Test = 14128
Errors = 0

maxThreads 200.zip
2.90MB

 

5) maxThreads 250 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 257.2 (max 302.5)
Response Time = 391.91
Executed Test = 14473
Errors = 1
TPS = 279.7 (max 334.0)
Response Time = 361.43
Executed Test = 15721
Errors = 1
TPS = 309.5 (max 374.0)
Response Time = 324.01
Executed Test = 17414
Errors = 1
maximumPoolSize 10 TPS = 224.0 (max 316.5)
Response Time = 449.75
Executed Test = 12606
Errors = 0
TPS = 245.4 (max 298.0)
Response Time = 412.11
Executed Test = 13797
Errors = 0
TPS = 286.7 (max 352.5)
Response Time = 349.92
Executed Test = 16126
Errors = 0
maximumPoolSize 15 TPS = 228.5 (max 258.0)
Response Time = 442.32
Executed Test = 12849
Errors = 0
TPS = 227.9 (max 266.0)
Response Time = 443.33
Executed Test = 12820
Errors = 0
TPS = 275.3 (max 334.5)
Response Time = 368.32
Executed Test = 15489
Errors = 0
maximumPoolSize 20 TPS = 218.1 (max 261.0)
Response Time = 462.14
Executed Test = 12270
Errors = 1
TPS = 246.1 (max 277.5)
Response Time = 410.51
Executed Test = 13844
Errors = 0
TPS = 257.2 (max 309.5)
Response Time = 391.01
Executed Test = 14484
Errors = 0
maximumPoolSize 25 TPS = 214.5 (max 264.5)
Response Time = 468.49
Executed Test = 12073
Errors = 1
TPS = 240.0 (max 285.0)
Response Time = 418.74
Executed Test = 13499
Errors = 1
TPS = 260.8 (max 345.5)
Response Time = 383.18
Executed Test = 14680
Errors = 1

maxThreads 250.zip
2.90MB

 

6) maxThreads 300 기준

  등록날짜 기준 좋아요 개수 기준 리뷰 개수 기준
maximumPoolSize 5 TPS = 222.0 (max 267.5)
Response Time = 450.57
Executed Test = 12494
Errors = 0
TPS = 266.0 (max 296.5)
Response Time = 379.46
Executed Test = 14966
Errors = 1
TPS = 295.7 (max 349.5)
Response Time = 340.11
Executed Test = 16632
Errors = 0
maximumPoolSize 10 TPS = 226.7 (max 261.5)
Response Time = 444.77
Executed Test = 12757
Errors = 1
TPS = 233.4 (max 276.0)
Response Time = 431.53
Executed Test = 13138
Errors = 1
TPS = 270.6 (max 325.0)
Response Time = 372.30
Executed Test = 15233
Errors = 1
maximumPoolSize 15 TPS = 218.0 (max 250.0)
Response Time = 461.66
Executed Test = 12277
Errors = 1
TPS = 243.5 (max 311.0)
Response Time = 414.29
Executed Test = 13696
Errors = 1
TPS = 280.2 (max 326.5)
Response Time = 360.37
Executed Test = 15764
Errors = 1
maximumPoolSize 20 TPS = 255.2 (max 303.0)
Response Time = 395.12
Executed Test = 14360
Errors = 0
TPS = 266.9 (max 304.5)
Response Time = 376.01
Executed Test = 15007
Errors = 0
TPS = 269.0 (max 308.5)
Response Time = 373.01
Executed Test = 15127
Errors = 0
maximumPoolSize 25 TPS = 206.6 (max 249.0)
Response Time = 487.26
Executed Test = 11624
Errors = 1
TPS = 230.3 (max 267.0)
Response Time = 436.71
Executed Test = 12967
Errors = 1
TPS = 262.4 (max 303.5)
Response Time = 385.40
Executed Test = 14764
Errors = 0

maxThreads 300.zip
2.90MB

 

여러 케이스별로 부하테스트를 진행해봄으로써 현재 프로젝트상에서 가장 최적의 조합은 Tomcat maxThreads 250 & HikariCP maximumPoolSize 5로 도출되었다

 

 

의문점

maxThreads & maxConnections이 많아지면 오히려 성능이 왜 떨어지는걸까?

 

일단 Thread도 리소스이고 각 쓰레드별로 Stack, PC Register, ..등이 별도로 할당된다

따라서 Thread가 점차 많아진다고 하면 결국 메모리도 그만큼 사용하게 되는 것이다

 

그리고 Thread가 점차 많아지면 Thread간의 Context Switching이 굉장히 빈번하게 발생할 수 있고 이러한 Context Switching에 대한 비용이 오버헤드가 될 수 있다

 

 

돈 때려박아서 메모리 늘리면 안됨?

 

RAM을 늘린다고 해도 CPU Core에 한계가 존재하기 떄문에 동시에 처리할 수 있는 Task역시 한계가 존재한다

  • CPU Core가 8개라면 최대 8개의 Thread만 동시에 처리할 수 있다
  • 우리 눈에 여러 Task가 진짜 동시에 실행되는것처럼 보이는 이유는 굉장히 짧은 Time Quantum에 의해 여러 Thread간의 Context Switching이 발생하기 때문이다

 

그리고 단순히 ThreadPool만 고려하는게 아니라 DBCP Connection또한 고려해야 한다

  • 물론 외부 Network I/O가 존재한다면 그에 대한 병목도 당연히 고려 대상

 

따라서 결론적으로 Thread, Connection수가 점차 많아진다고 하더라도 성능이 그에 비례적으로 좋아지는게 아니라 오히려 저하될 수 있다