Hello World

자바 웹 애플리케이션­­­ 성능에 영향을 미치는 대표적인 문제 유형 본문

Back-End/좋은글

자바 웹 애플리케이션­­­ 성능에 영향을 미치는 대표적인 문제 유형

EnterKey 2016. 8. 10. 17:57
반응형

웹의 시대에서는 막대한 트래픽(요청)이 발생하면 그 만큼의 수익이 나는 것이 어느 정도 가능했으며(광고 수익, 제품 구매 등…), 흔히 말하는 Enterprise 기업들을 위한 제품 Oracle과 여러 상용 WAS를 구입하여 많은 문제(예: 세션 클러스터링 등…)를 기본적으로 해결된 전제에서 시작했습니다.

하지만 모바일의 시대가 대두되면서, 사용자가 늘어난 만큼 매출이 발생되지 않는 상황이 발생했습니다. 금액적인 압박이 훨씬 크기 때문에 Oracle과 상용 제품을 쓸 수 없으니, 다양한 오픈소스를 활용하게 되었으며, Micro Service Architecture 등이 하나의 패러다임으로 많은 개발자들에게 논해지면서 더욱더 백엔드의 복잡도가 증가되는 것이 현재의 상황입니다.

그림 1. 백엔드의 어려움을 표현한 그림

이러한 상황을 풍자한 재미난 그림도 있습니다.

백엔드의 성능이 저하되거나 장애가 발생하면 서비스의 품질이 낮아지고 고객 이탈을 유발하기 때문에 갈수록 성능 모니터링 역시 중요해지고 있습니다. (사족: 실례로 MSA를 적용한 기업들을 만났을 때 가장 고통스러워하는 것이 하나의 긴 트랜잭션에서 병목구간이 어딘지 알 수 없다는 것이었습니다.  그래서 Zipkin과 같은 오픈소스를 활용하여 병목구간을 찾기위해 여러 노력을 하시는 분도 있습니다.)

와탭은 오랜 경험을 거쳐 백엔드 성능에 영향을 주는 대표적인 문제 유형들을 설명 드리고자 합니다. 그 중에서도 자바 웹 애플리케이션에서 발견되는 대표적인 문제 유형들에 대해 소개하겠습니다.

transaction_img요청이 발생한 경우 수행되는 트랜잭션에서 병목이 되는 구간은 다음과 같은 카테고리로 묶을 수 있습니다.

  • Database 연결 및 쿼리 수행
  • 내부 로직 수행
  • 외부 호출 (HTTP, Socket 통신 등)
  • 그 외 환경 설정이나 네트워크 등의 문제

Database 연결 및 쿼리 수행 시 발견되는 문제 유형

전통적으로 장애의 가장 큰 원인을 차지하는 것은  Database입니다.  Database에 연결하기 위해 Connection객체를 사용하거나 Query를 수행하는 로직에서 성능 저하, 장애가 가장 많이 발견됩니다.

1) Slow Query / DB Lock

Slow Query, 과도한 업데이트, 인덱싱이 발생할 경우 Database는 Lock이 발생합니다. Database에 Lock이 발생하면, 해당 Row에 접근하는 요청들은 Lock이 풀릴 때까지 대기하거나 Timeout 됩니다. 이는 웹 애플리케이션 성능이 저하되는 대표적인 원인이며 이로 인해 장애가 발생하기도 합니다.

2) 불 필요한 DB Fetch

요청하는 데이터의 건수가 많을 수록 응답 시간이 느려집니다. 실제 필요한 데이터는 상위 몇 개인데  대량의 데이터를 요청하였거나, ResultSet에서 Last로 커서로 이동시키는 작업들이 빈번해지면 응답시간이 현저히 느려지고 Out Of Memory가 발생합니다. ORM에서 생성된 쿼리들도 주의깊게 보셔야 합니다.

 3) Connection Leak / 부적절한 Connection Pool size

DBMS들은 클라이언트가 요청을 할 때마다 매번 Connection 객체를 생성하지 않습니다. 요청할 때마다 Connection 객체를 생성할 경우 생성 비용이 만만치 않기 때문에,  Connection Pool에서 미리 생성된 Connection을 꺼내 사용하게 되어 있습니다.

문제의 핵심은 Connection Pool 사이즈를 너무 작거나 너무 크게 설정한 경우 성능에 안 좋은 영향을 미칠 수 있다는 것입니다. 특히, Connection의 개수를 설정하지 않으면 10~20 정도의 default값이 적용되기 때문에 동시 사용자가 많은 경우에는 Connection의 개수를 그에 맞게 적절히 조절해야 합니다. (너무 과하게 Connection Pool Size를 잡으면 과도한 메모리를 사용하겠죠.)

자바에는 Connection, Statement, ResultSet 등DB Connection과 관련된 JDBC 객체가 존재합니다. 이러한 객체는 사용한 후 close를 하지 않으면 Connection이 Pool에 반환되지 않아 Connection Leak이 발생되며, 부작용으로 DB에 접근하는 트랜잭션들이 Connection을 얻지 못 하고 대기하여 요청 시간이 현저히 느려지거나, 최악의 경우에는 장애가 발생할 수 있습니다.

4) 그 외

그 외에도 트랜잭션이 commit을 하지 않고 Connection Pool에 반환되거나, 잘못된 코드로 불필요한 commit이 빈번하게 발생하는 경우에도 성능에 영향을 줄 수 있으니 코드 작성 시 주의해야 합니다.

간혹 느린 쿼리가 튜닝의 대상이 아닌 경우도 있습니다. 실행 시간이 느리지만 튜닝이 더이상 되지 않는 (느린 것을 감수해야하는) 쿼리도 있고, 실행 시간이 빠르지만 빈번하게 호출되며 튜닝의 여지가 있는 쿼리도 있습니다. 이런 경우에는 후자를 튜닝해야 전체적인 성능을 개선시킬 수 있기 때문에, 전체 트랜잭션의 분포도의 관점으로 바라보아야 합니다. (분포도라는 관점을 고려해서 와탭의 히트맵이 만들어졌는데요, 왜 이렇게 설계되었는지 추후 성능적인 글로 설명드리겠습니다.)

성능에 영향을 미치는 문제는 데이터베이스와 관련된 문제가 가장 자주 발생하며 그 원인 또한 다양하게 나타납니다. 로그 트래킹, Slow Query를 수집 하더라도 원인이 숨겨져 있는 경우가 많고, 비즈니스의 전체적인 성능을 모니터링 하기는 어렵기 때문에 APM(Application Performance Monitoring)으로 웹 애플리케이션의 성능을 분석하고 모니터링 해야합니다.

외부 호출 (HTTP, 소켓 통신 등) 

트랜잭션 안에 포함된 또 다른 외부 트랜잭션(외부 호출)이 긴 시간 수행된다거나,   Timeout이 발생하는 경우 성능 저하가 발생합니다. 예를 들어 택시 호출 앱을 호출했을 경우, 위치 정보를 얻기 위해 지도 서비스를 호출해야 되는 경우이지요.

이런 경우의 대답은 이중화 입니다. 하나의 외부 서비스(결재 서비스 A)가 주어진 데드라인에 응답하지 않는다면, 대안 서비스인 결재 서비스 B, C를 호출하는 이중화 전략들이 필요합니다.

e2e monitoring
그림 3. Zipkin의 실행 모습

MSA(Micro Service Architecture)가 인기있는 프로그래밍 패러다임으로 전환되면서 그림 3과 같은 End2End Transaction 모니터링에 대한 강한 니즈가 생기게 되었습니다. (사실 금융권에서는 거래 추적이라는 이름으로 예전부터 지원되었습니다.)

긴 시간 실행되는 내부 로직 수행

내부 로직으로 인해 성능 저하가 발생하는 경우도 종종 볼 수 있습니다. 특히 모바일 상황이 되면서 완료되지 않는 트랜잭션들이 존재하는 상황이 비일비재합니다.

  • 모바일 애플리케이션에서, 데이터를 다 전송받지 못 하고 인터넷 접속이 불능 상태가 되었을 때 (서버 소켓 Write는 계속 일어나고 있으나, 모바일 애플리케이션이 비정상적으로 Read가 수행되지 않은 경우)
  • 특정 파일을 업로드 하거나, 다운로드 하는 상황 등
  • 예외 처리를 제대로 하지 않아 행이 걸리는 상황 등

이러한 경우는 이 당시의 Call Stack 정보를 얻어 오는 게 중요하며, 와탭은 10초마다 실행 중인 모든 트랜잭션의 메모리 스택을 추출함으로서, 통계적으로 느린 내부 로직을 우선순위화 하여 보여줍니다.

그 외 환경 설정이나 네트워크 등의 문제

  • 너무 작게 설정된 Thread Pool
  • 너무 작게 설정된 Heap Memory
  • 과도한 서비스 요청 증가 (처리 능력 초과)
  • 네트워크 장비로 인한 데이터 손실

이상으로 성능에 영향을 주는 병목지점들을 설명해 드렸습니다. 다음 포스트에는 병목구간을 가지고 있는 몇 개의 Problem Set (샘플코드)들을 실행하여, 다양한 SaaS APM에서  비교/분석해 보는 시간을 가지도록 하겠습니다.

반응형
Comments