2장. 느려진 서비스, 어디부터 봐야 할까
응답 시간과 처리량
성능이 저하되면 가장 눈에 띄는 현상은 결과가 늦게 표시되는 것이다. 너무 오래 걸려서 타임아웃 에러가 발생하기도 한다.
사용자는 무언가를 실행할 때 동작하기까지 걸린 시간으로 성능을 판단하지만 실제로는 다양한 지표가 성능과 관련되어 있다. 다양한 지표 중에서 서버 성능과 관련 있는 중요한 지표 2가지를 꼽자면 응답 시간과 처리량을 들 수 있다.
응답 시간(Response Time)
응답 시간은 사용자의 요청을 처리하는 데 걸리는 시간을 의미한다.

API 요청부터 JSON 응답까지 소요되는 시간을 응답 시간(Response Time)이라고 한다.
응답 시간은 2가지로 나누어 측정하기도 한다.
TTFB (Time to First Byte): 응답 데이터 중 첫 번째 바이트가 도착할 때까지 걸린 시간
TTLB (Time to Last Byte): 응답 데이터의 마지막 바이트가 도착할 떄까지 걸린 시간
응답 데이터의 크기가 작다면 TTFB와 TTLB의 차이가 크지 않다. 하지만 파일 ㅏ운로드처럼 전송할 데이터가 크거나 네트워크 속도가 느리면 TTFB와 TTLB의 차이가 커질 수 있다.
따라서 서버 성능을 올바르게 평가하려면 데이터 특성이나 네트워크 환경을 고려해 적절한 지표를 선택해 측정해야 한다.
위의 그림처럼 응답 시간은 세 단계로 나뉘게 된다.
API 요청 전송 시간
서버의 처리 시간
API 응답 전송 시간
서버 개발자는 주로 서버의 처리 시간을 확인한다.
로직 수행
DB 연동 (SQL 실행)
외부 API 연동
응답 데이터 생성/전송
특히, 이 중에서도 DB 연동과 외부 API 연동이 처리 시간에 큰 비중을 차지하게 된다. 이러한 이유로 응답 시간을 줄일 때 DB 연동과 API 연동 시간에 집중한다.
처리량(Throughput)
처리량은 단위 시간당 시스템이 처리하는 작업량을 의미하는데 흔히 TPS나 RPS로 처리량을 나타낸다.
TPS(Transaction Per Second): 초당 트랜잭션 수
RPS(Request Per Second): 초당 요청 수
성능을 개선하려면 먼저 현재 서버의 TPS와 응답 시간을 알아야 한다. 막연히 성능이 느리다고 말하면서 이것저것 시도하면 안된다.
트래픽이 많은 시간대의 TPS와 응답 시간을 파악한다.
이 결과를 바탕으로 목표 TPS와 응답 시간을 설정하고 효과적인 개선안을 도출한다.
이러한 흐름으로 성능 개선 흐름을 잡아가자.
TPS를 확인하는 가장 간단한 방법은 모니터링 시스템을 활용하는 것이다. 실시간 TPS뿐 아니라 과거 특정 시점의 TPS도 확인할 수 있다. 참고로 많은 모니터링 시스템이 TPS를 구할 때 근사치를 사용한다.
더욱 정확한 TPS를 알고 싶다면 웹 서버 접근 로그를 활용하자. 엘라스틱 서치 같은 별도 시스템에 접근 로그를 수집한 뒤 집계하면 된다. 만약 이런 도구가 없다면 접근 로그를 파싱해서 TPS를 구해도 된다.
서버 성능 개선 기초
병목 지점
서비스 초기에는 성능 문제가 잘 발생하지 않는다. 왜냐? 사용자 수, 트래픽, 데이터베이스 크기 등 모두 작기 때문이다. 트래픽이 늘고 데이터가 쌓이면서 간헐적으로 응답 시간이 느려지는 현상이 발생하는 것이다.
트래픽이 증가하면서 성능 문제가 발생하는 주된 이유는 시스템이 수용할 수 있는 '최대 TPS'를 초과하는 트래픽이 유입되기 때문이다.
최대 TPS: 시스템이 처리할 수 있는 최대 요청 수
당연하게도 TPS를 높이려면 처리 시간이 오래 걸리는 작업을 식별하면 된다. 성능 문제는 응답 시간이 길어지면서 발생하는 경우가 많기 때문이다.
수직 확장과 수평 확장
만약 성능 문제를 일으키는 원인을 찾았다면 빠르게 적용할 수 있는 개선안을 도출해야 한다. 사용자가 서비스를 이용하지 못하는 상황을 방치한 채 시간만 오래 걸리는 개선 방식을 시도할 순 없다.
수직 확장(scale-up)
수직 확장(scale-up)이라는 방식을 통해 일단 급한 불을 끄고 근본적인 해결책을 모색하자.
수직 확장이 만능은 아니다. 즉각적인 효과를 바로 얻을 수 있으며, 쉽게 적용할 수 있지만 트래픽이 지속해서 증가한다면 언젠가 결국 또다시 성능 문제가 발생하게 될 것이다. 수직 확장은 비용이 많이 든다! 또한, 한 대의 장비가 감당할 수 있는 용량에도 한계가 있다.
수평 확장(scale-out)
따라서 트래픽이 증가하면 수평 확장(scale-out)을 통해 TPS를 높이는 방법도 고려해야 한다.
그렇다고 TPS를 높이기 위해 무작정 서버를 추가해서도 안 된다. 실제 병목 지점이 어디인지 파악하는 것이 가장 중요하다.
만약 DB에서 성능 문제가 발생하고 있는데 서버를 추가로 투입하면 불에 기름을 붓는 격이다.
외부 API의 성능 문제인 경우도 마찬가지다. 서버를 추가한다고 나아질까?
즉, DB나 외부 API에 성능 문제가 발생하지 않는 범위 내에서만 수평 확장을 해야 효과가 있다.
서버 캐시
응답 시간을 줄이고 처리량을 높이기 위해 DB 서버를 수직 확장하거나 수평 확장을 할 수 있다. 하지만, DB 서버를 확장하려면 비용이 많이 들며 수평 확장을 하더라도 처리량은 늘릴 수 있지만 실행 시간은 획기적으로 줄어들지 않는다.
이럴 땐 캐시(cache) 사용을 고려할 수 있다.
캐시 사전 적재
트래픽이 순간적으로 급증하는 패턴을 보인다면 캐시에 데이터를 미리 저장하는 것도 고려할 필요가 있다.
아래와 같은 가상의 사례를 생각해보자.
G 앱 사용자는 300만 명이다.
G 앱 서비스는 사용자에게 매달 정해진 날에 이달의 요금 정보를 보여준다.
해당 일자가 되면 전체 회원을 대상으로 요금 안내 푸시 알림을 발송한다.
푸시를 받은 사용자 중 일부는 G 앱을 통해 이달의 요금 정보를 조회한다.
이 후 많은 사용자가 동시에 접속한다면? 캐시에 데이터가 없기 때문에 전체 응답 시간이 느려질 뿐만 아니라 DB에 전달되는 부하도 급격히 증가하게 되는 것이다.
이런 상황을 방지하기 위해 캐시에 데이터를 미리 넣어둔다면 캐시 적중률도 99% 가깝게 유지할 수 있으며, 트래픽이 몰렸을 때도 응답 시간을 안정적으로 유지할 수 있고, DB에 부하가 집중되는 현상도 효과적으로 방지할 수 있다.
관련 글
캐시 무효화
캐시를 사용할 때 반드시 신경 써야 할 점은 유효하지 않은 데이터를 적절한 시점에 캐시에서 삭제하는 것이다. 캐시 신선도라고 불렀던 것 같다.
게시글 내용을 수정했는데도 캐시가 그대로 유지된다면 사용자는 수정 전 게시글 내용을 보게되어 혼란을 겪을 수 있다. 이는 서비스에 대한 신뢰도를 낮게 평가할 수도 있게 된다.
변경에 민감한 데이터는 데이터 정합성을 맞추기 위해 로컬 캐시가 아닌 리모트 캐시에 보관하자.
변경에 민감하지 않고 데이터 크기가 작다면 캐시의 유효 시간을 설정하여 주기적으로 갱신하자.
대기 처리
콘서트 예매처럼 사용자가 순간적으로 폭증할 경우가 있다. 이렇게 ㅉ랍은 시간 동안 폭증하는 트래픽은 어떻게 처리해야 할까?
서버를 미리 증설한다.
수용할 수 있는 수준의 트래픽만 받아들이고 나머지는 대기 처리한다.
서버를 증설하는 경우
서버를 증설하는 것은 클라우드를 사용하고 있다면 쉽게 증설할 수 있다. 하지만, 서버만 증설하는 것에 그치지 않고 DB 성능도 문제가 된다. 트래픽은 DB에도 영향을 미치기 때문이다. 또한, DB는 쉽게 증설할 수 없어 예상되는 트래픽에 맞춰 미리 증설해야 한다.
물론, 순간적으로 폭증하는 트래픽을 처리하기 위해 서버와 DB를 증설하는 것도 잘못된 방법은 아니지만, '비용'적인 문제가 크다. 짧은 시간을 버티기 위해서 투입해야 하는 비용이 크기 때문이다.
대기 처리하는 경우
은행 창구에서 고객을 응대하는 것 처럼 대기시키는 것을 생각하면 쉬울 것 같다.
트래픽이 순간적으로 증가할 때 동시에 수용할 사용자 수를 제한하고 나머지 사용자를 대기 처리하면 다음과 같은 이점을 얻는다.
서버를 증설하지 않고도 서비스를 안정적으로 제공할 수 있다.
사용자의 지속적인 새로 고침으로 인한 트래픽 폭증도 방지할 수 있다. 사용자는 새로 고침할 경우 순번이 뒤로 밀리기 때문에 불필요한 새로 고침을 자제하게 된다.
개발자라면 대규모 트래픽을 처리할 수 있는 아키텍처를 경험해보고 싶겠지만, 지불해야할 비용이 결코 적지 않다는 점을 생각하자. 이러한 관점에서 대기 처리 방식은 매우 합리적인 방식이며 대기 제어 기능을 제공하는 솔루션이 많이 있다는 것도 장점이다.
Last updated
Was this helpful?