Design/Designing Data-Intensive Applications

신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션 (2)

Seung-o 2024. 7. 17. 23:21

확장성

 

부하 기술하기

 

성능 저하를 유발하는 흔한 이유 중 하나는 부하 증가다. 확장성은 증가한 부하에 대처하는 시스템 능력을 설명하는 데 사용하는 용어지만 시스템에 부여하는 일차원적인 표식은 아니다. 단순은 "~은 확장 가능하다" 와 같은 말은 의미가 없다. 오히려 확장성을 논한다는 것은 "시스템이 특정 방식으로 커지면 이를 대처하기 위한 좋은 조치는 무엇일까", "추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까" 같은 질문을 고려한다는 의미다.

 

저자는 트위터의 트윗 처리 방식을 대표적인 예시로 말한다. 

 

트위터의 주요 동작은 크게 두 가지, (1) 트윗 작성과 (2) 홈 타임라인 조회이다. 트윗 작성은 초당 46,000번 발생하고, 홈 타임라인 조회는 초당 300,000번 발생한다. 사실 이 정도의 요청을 단순히 처리하는 것은 어려운 일이 아니라고 한다. 어려워 보이는데.. 그보다도 어려운 것은 트위터의 "팔로우" 기능에서 기인한다. 

 

내가 만약 트윗을 1개 작성했다고 하자. 나를 팔로우하는 사람이 100만명이면, 100만명 사람들이 홈 타임라인을 조회할 때 내 트윗이 나오도록 해야한다. 이것을 "팬 아웃 ( Fan-out )" 현상이라고 한다. 

 

이것을 구현하기 위해서는 두 가지 방법을 떠올릴 수 있다.

 

1) 내가 작성한 트윗을 데이터베이스에 삽입한다. 그리고 내 팔로워들이 홈 타임라인을 조회할 때, 데이터베이스로부터 데이터를 가져온다. 

 

2) 팔로워마다 캐시 형태의 우편함을 갖고 있다. 내 트윗이 게시되면 각 팔로워의 우편함 캐시에 내 트윗을 추가한다. 이 경우, 홈 타임라인의 읽기 요청은 요청 결과를 미리 계산했기 때문에 비용이 저렴하다.

 

 

조금 더 직관적이고 쉬운 방법은 1번이다. 사실 요청이 그리 많지 않은 서비스라면 1번처럼 운용해도 문제될 것이 그다지 없다. ( 실제로 트위터의 첫 번째 버전이기도 하다 )

 

하지만 트위터의 상황은 다르다. 평균적으로 트윗 게시 요청량이 홈 타임라인 읽기 요청량에 비해 수백 배 적기 때문에 2번 방식이 훨씬 동작하다. 2번 방식의 불리한 점은 트윗 작성이 많은 부가 작업을 필요로 한다는 것이다. 가령, 나는 1개의 트윗을 작성했지만, 캐시를 갱신하는 작업은 100만번 일어나야 하는 셈이다. 

그럼에도 불구하고, 100만번의 쓰기 작업이 늦게 이루어진다고 해서 트윗을 작성한 나 혹은 나의 팔로워가 불편을 겪을 확률은 지극히 낮다. 오히려, 빠른 쓰기는 되었어도 홈 화면의 로딩이 하루 종일 돌아가고 있는 것이 더욱 불편한 일이 될 것이다. 

 

위 트위터의 사례에서, 결국 이들에게 부하를 결정하는 요소(이하 부하 매개변수)는 "팔로워 분포"가 된다. 애플리케이션마다 특성은 매우 다르지만 부하에 대한 추론에 비슷한 원리를 적용해볼 수 있다.

 

 

성능 기술하기

 

시스템의 성능을 기술하는 방법은, 시스템의 종류에 따라 달라질 수 있다. 하둡(Hadoop) 과 같은 일괄 처리 시스템은 "처리량"을 통해 성능을 기술한다. 가령, 초당 몇 개의 레코드를 처리했는지 또는 N개의 레코드를 몇 초 내로 처리할 수 있는지 등이다. 반면, 온라인 시스템에서는 "응답 시간", 즉 요청을 보내고 응답을 받기까지에 소요되는 시간이 중요하다. 

 

응답 시간은 보통 요청마다 일정하지 않다. 요청이 들어온 순간의 네트워크, 서버 리소스, GC 작동 등에 따라 달라진다. ( 실제로 APM 등에서 운용하고 있는 제품의 응답 시간 그래프를 확인해본다면 쉽게 확인이 가능하다 ) 이에 대한 샘플은 대략 아래와 같다. 

 

응답 시간 그래프 샘플

 

성능이라는 것은 "얼마나 많은 사용자들이 실제로 지연을 어느 정도 경험했는지"와 직결된다. 이 때는 산술 평균 ( arithmetic mean )을 살펴보는 것보다는, 백분위 ( percentile )를 사용하는 편이 더 좋다. 예를 들어, p50은 중앙값 ( median)이 된다. 중앙값이 200 ms라면, 절반의 유저는 200ms 보다 빠른 응답을 받고, 나머지는 그보다 느린 응답을 받았다는 것으로 해석할 수 있다. 

 

실제로 서비스에서는 "특이값"을 많이 관찰한다. 이 때, 자주 사용하는 백분위는 95 percentile, 99 percentile, 99.9 percentile이다. 예를 들어, 95 percentile 응답 시간이 1.5초라면, 100개의 요청 중 95개는 1.5초 미만이고, 5개는 그 이상이라는 것을 의미한다. 

 

실제로 꼬리 지연 시간 ( tail latency )은 서비스의 사용자 경험에 직접 영향을 주기 때문에 중요하다. 예를 들어, 아마존은 내부 서비스의 응답 시간 요구사항을 99.9 percentile 로 기술한다고 한다. 보통 응답 시간이 가장 느린 요청을 경험한 고객이 많은 구매를 해서 고객 중 많은 데이터를 갖고 있기 때문이라고 한다. 실제로, 아마존은 응답 시간이 100 ms 증가하면 판매량이 1% 줄어들고, 고객 만족 지표는 16% 줄어든다는 현상을 관찰했다고 한다.