2022년 7월 16일 토요일

Spring: Blocking vs non-blocking: R2DBC vs JDBC and WebFlux vs Web MVC


2017년 9월에 출시된 Spring Framework 버전 5에는 Spring WebFlux가 도입되었습니다. 완전히 반응하는 스택. 2019년 12월, 반응형 드라이버를 사용하여 관계형 데이터베이스를 통합하는 인큐베이터인 Spring Data R2DBC가 출시되었습니다. 이 블로그 게시물에서 나는 높은 동시성에서 WebFlux와 R2DBC가 더 나은 성능을 보인다는 것을 보여줄 것입니다. 더 나은 응답 시간과 더 높은 처리량을 제공합니다. 추가 이점으로 처리된 요청당 메모리와 CPU를 덜 사용하고 (R2DBC의 경우 JPA를 생략할 때) 뚱뚱한 JAR이 훨씬 작아집니다. 높은 동시성에서는 WebFlux와 R2DBC(JPA가 필요하지 않은 경우)를 사용하는 것이 좋습니다!

방법

이 블로그 게시물에서는 4가지 구현을 살펴보았습니다.

  • Spring Web MVC + JDBC 데이터베이스 드라이버
  • Spring Web MVC + R2DBC 데이터베이스 드라이버
  • Spring WebFlux + JDBC 데이터베이스 드라이버
  • Spring WebFlux + R2DBC 데이터베이스 드라이버

진행 중인 요청 수(동시성)를 4에서 500까지 50단계로 변경하고 부하 생성기와 서비스에 4개의 코어를 할당했습니다(내 랩톱에는 12개의 코어가 있음). 모든 연결 풀을 100으로 구성했습니다. 코어 수와 연결 풀 크기가 고정된 이유는 무엇입니까? JDBC 대 R2DBC 데이터에 대한 이전 탐색에서 이러한   변수를 변경하는 것은 많은 추가 통찰력을 제공하지 않았기 때문에 이 테스트를 위해 고정된 상태로 유지하기로 결정하여 몇 가지 요인에 의해 테스트 실행 기간을 줄였습니다.

서비스에 대해 GET 요청을 했습니다. 서비스는 데이터베이스에서 10개의 레코드를 가져와 JSON으로 반환했습니다. 먼저 서비스에 무거운 부하를 가하여 2초 동안 서비스를 '프라이밍'했습니다. 다음으로 2분 벤치마크로 시작했습니다. 나는 모든 시나리오를 20번 반복하고(다른 테스트로 구분하여 5번이 아닌) 결과를 평균화했습니다. 오류가 발생하지 않은 실행만 보았습니다. 동시성을 1000 이상으로 늘리면 모든 구현에서 예외 없이 추가 동시 요청이 실패했습니다. 결과는 재현 가능한 것으로 나타났습니다.

백엔드 데이터베이스로 Postgres(12.2)를 사용했습니다. 나는  wrk  를 사용하여 구현을 벤치마킹했습니다(여러 권장 사항으로 인해). 여기 에서 다음을 사용하여 wrk 출력을 구문 분석 했습니다 나는 측정했다

  • 응답 시간
    wrk에서 보고한 대로
  • 처리량(요청 수)
    wrk에서 보고한 대로
  • CPU 사용량 처리
    사용자 및 커널 시간(/proc/PID/stat 기준)
  • 메모리 사용량
    개인 및 공유 프로세스 메모리(/proc/PID/maps 기반)

여기에서 사용된 테스트 스크립트를 볼 수 있습니다  여기에서 사용된 구현을 볼 수 있습니다  .

결과

여기 에서 그래프에 사용한 원시 데이터를 볼 수 있습니다  .

응답 시간

더 높은 동시성에서는 Spring Web MVC + JDBC가 최선의 선택이 아닐 수 있음이 분명합니다. R2DBC는 더 높은 동시성에서 더 나은 응답 시간을 분명히 제공합니다. Spring WebFlux는 또한 Spring Web MVC를 사용하는 유사한 구현보다 더 나은 성능을 제공합니다.

처리량

응답 시간과 유사하게 JDBC를 사용하는 Spring Web MVC는 더 높은 동시성에서 더 나빠지기 시작합니다. 다시 R2DBC가 최선을 다합니다. 백엔드가 여전히 JDBC인 경우 Spring Web MVC에서 Spring WebFlux로 이동하는 것은 좋은 생각이 아닙니다. 낮은 동시성에서는 Spring Web MVC + JDBC가 가장 좋습니다.

CPU

CPU는 전체 실행 중 CPU 시간, 프로세스 사용자 및 커널 시간의 합으로 측정되었습니다.

JDBC가 있는 웹 MVC는 높은 동시성에서 대부분의 CPU를 사용했습니다. JDBC를 사용하는 WebFlux는 가장 적게 사용되었지만 처리량도 가장 낮았습니다. 처리된 요청당 사용된 CPU를 보면 효율성을 측정할 수 있습니다.

R2DBC는 JDBC보다 처리된 요청당 더 적은 CPU를 사용합니다. JDBC를 사용하는 WebFlux는 (다시) 좋은 생각이 아닌 것 같습니다. JDBC를 사용하는 웹 MVC는 높은 동시성에서 악화되는 반면, 하나 이상의 비차단 구성 요소가 있는 다른 구현은 더 안정적으로 보입니다. 그러나 낮은 동시성에서 Web MVC + JDBC는 사용 가능한 CPU를 가장 효율적으로 사용할 수 있습니다.

메모리

메모리는 실행이 끝날 때 프로세스 개인 메모리로 측정되었습니다. 메모리 사용량은 가비지 수집에 따라 다릅니다. G1GC는 JDK 11.0.6에서 사용되었습니다. Xms는 0.5Gb였습니다(기본값은 사용 가능한 32Gb의 1/64). Xmx는 8Gb였습니다(기본값은 사용 가능한 32Gb의 1/4).

WebFlux는 더 높은 동시성에서 더 높은 메모리 사용량을 갖는 Web MVC와 비교할 때 메모리 사용량이 더 안정적인 것으로 보입니다. WebFlux를 사용할 때 R2DBC도 함께 사용하면 높은 동시성에서 메모리 사용량을 최소화할 수 있습니다. 낮은 동시성에서 Web MVC + JDBC가 가장 잘 수행되지만 높은 동시성에서 WebFlux + R2DBC는 처리된 요청당 최소 메모리를 사용합니다.

뚱뚱한 JAR 크기

아래 그래프는 JPA가 큰 것임을 보여줍니다. R2DBC의 경우 사용할 수 없으면 뚱뚱한 JAR 크기가 15Mb 정도 줄어듭니다!

요약

R2DBC 및 WebFlux, 높은 동시성에서 좋은 아이디어!

  • 높은 동시성에서 JDBC 대신 R2DBC를 사용하고 Web MVC 대신 WebFlux를 사용하는 이점은 분명합니다. 
    • 단일 요청을 처리하는 데 더 적은 CPU가 필요합니다. 
    • 단일 요청을 처리하는 데 필요한 메모리가 적습니다. 
    • 높은 동시성의 응답 시간이 더 좋습니다.
    • 높은 동시성에서의 처리량이 더 좋습니다.
    • 뚱뚱한 JAR 크기가 더 작습니다(R2DBC가 있는 JPA 없음).
  • 차단 구성 요소만 사용하는 경우 높은 동시성에서 메모리 및 CPU 사용 효율성이 떨어집니다.
  • JDBC를 사용하는 WebFlux는 좋은 생각이 아닌 것 같습니다. R2DBC를 사용하는 웹 MVC는 JDBC를 사용하는 웹 MVC보다 높은 동시성에서 더 잘 작동합니다.
  • R2DBC 사용의 이점을 얻기 위해 완전히 비차단 스택이 필요하지 않습니다. 그러나 Spring의 경우 WebFlux와 결합하는 것이 가장 좋습니다.
  • 낮은 동시성(200개의 동시 요청 미만)에서 Web MVC 및 JDBC를 사용하면 더 나은 결과를 얻을 수 있습니다. 이것을 테스트하여 자신의 손익분기점을 결정하십시오!

R2DBC를 사용할 때의 몇 가지 문제

  • JPA는 Spring Data R2DBC에서 제공하는 것과 같은 반응형 리포지토리를 처리할 수 없습니다. 즉, R2DBC를 사용할 때 수동으로 더 많은 작업을 수행해야 합니다.
  • Quarkus Reactive Postgres 클라이언트(Vert.x 사용)와 같은 다른 반응 드라이버가 있습니다. 이것은 R2DBC를 사용하지 않으며 다른 성능 특성을 갖습니다(  여기 참조 ).
  • 제한된 가용성
    모든 관계형 데이터베이스에 사용 가능한 반응 드라이버가 있는 것은 아닙니다. 예를 들어, Oracle에는 (아직?) R2DBC 구현이 없습니다. 
  • 애플리케이션 서버는 여전히 JDBC에 의존합니다.
    이 Kubernetes 시대에 사람들은 여전히 ​​레거시가 아닌 용도로 사용합니까?
  • Java Fibers가 도입될 때(Project Loom, Java 15일 수 있음) 드라이버 환경이 다시 변경될 수 있고 R2DBC가 결국 JDBC의 후계자가 되지 않을 수 있습니다.

댓글 없음:

댓글 쓰기