프로그래밍/Backend

확장성의 의미와 트위터의 사례

카카수(kakasoo) 2022. 6. 19. 17:12
반응형

결론부터 말하자면, 시스템 설계에서 가장 적합한 부하 매개변수를 고른 정한 다음, 그걸 기준으로 부하를 판단해야 한다. 트위터에서는 팬 아웃이라는 용어를, 트윗 1건 당 팔로워에게 전송해야 하는 메시지의 수를 설명하는데, 이와 같은 SNS 사례는 서버 측이 겪을 수 있는 가장 큰 트래픽 중 하나를 보여준다.

 

 

확장성의 의미

시스템의 데이터 양, 트래픽, 복잡도가 증가하면서, 이를 처리할 적절한 방법들이 필요해진다.

 

시스템이 현재 안정적으로 동작한다고 해서 미래에도 안정적으로 동작할 거란 보장은 없다. 성능 저하를 유발하는 흔한 이유 중 하나는 부하 증가다. 사용자의 수는 단순히 10배 증가했다고 할 수 있지만, 그게 10만에서 100만으로, 100만에서 1,000만으로 증가하는 등, 절대적인 수에서도 큰 차이를 보일 수 있다. 시스템은 전에 처리해야 했던 양보다 더 많은 데이터를 처리해야 할 수 있다. 확장성은 증가한 부하에 대처하는 시스템 능력을 설명하는 용어지만, 시스템에 부여하는 일차원적인 표현이 아니다. 확장성을 논한다는 것은, 시스템이 특정 방식으로 커지면, “이에 대처하기 위한 선택은 무엇인가” 라는 질문과, “추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까?” 라는 질문을 고려한다는 의미를 가진다.

 

확장성을 논의하기 위해서는 먼저, 시스템의 현재 부하를 간결하게 기술해야 한다. 그래야 부하 성장 질문 ( ex. 부하가 두 배로 되면 어떻게 되는가? ) 을 논의할 수 있다. 부하는 부하 매개변수 ( load parameter ) 라고 부르는 몇 개의 숫자로 나타낼 수 있는데, 가장 적합한 부하 매개 변수의 선택은 시스템 설계에 따라 다르다. 부하 매개 변수로, 웹 서버의 초당 요청 수, 데이터베이스의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자 ( active user ), 캐시 적중률 등이 될 수 있다. 평균적인 경우가 중요할 수 있고, 소수의 극단적인 경우 병목 현상의 원인이 될 수 있어, 소수의 데이터가 더 중요할 수도 있다.

 

트위터 ( Twitter ) 의 예

  • 트윗 작성
    • 사용자는 팔로워에게 새로운 메시지를 게시할 수 있다. ( 평균 4,600회 요청, 피크일 때 12,000회 )
  • 홈 타임라인 ( timeline )
    • 사용자는 팔로우한 사람이 작성한 트윗을 볼 수 있다. ( 초당 300,000회 )

 

단순히 초당 12,000건의 쓰기 처리는 상당히 쉽다. 하지만 트위터의 확장성 문제는 주로 트윗 양이 아닌, 팬 아웃 ( fan-out ) 때문이다. ( 팬 아웃은 전자공학에서 빌려온 용어로, 다른 게이트의 출력에 배속된 논리 게이트 입력의 수를 뜻하는데, 여기서는 트랜잭션 처리 시스템에서 하나의 수신 요청을 처리하는 데 필요한 다른 서비스 요청 수를 의미한다. ) 좀 더 실제에 가까운 예를 들면, 개별 사용자가 많은 사람을 팔로우하고, 많은 사람이 개별 사용자를 팔로우한 경우, 각 개인의 트윗 메시지 작성은, 그 개별 사용자를 구독한 사람들에게 알림을 보내는 서비스 로직으로 이어지기 때문이다.

 

첫번째 방법

SELECT tweets.*, users.* FROM tweets
	JOIN users ON tweets.sender_id = users.id
	JOIN follows ON follows.followee_id = user.id
	WHERE follows.follower_id = current_user

 

실제로 구현하기 위해서, 우리는 이런 쿼리문을 작성할 수 있다. 현재 트윗에 작성자 정보를 JOIN한 다음, 모든 팔로워들을 조회하여 가져오는 방식이다. 트위터의 첫 방식은 이런 방식을 사용했는데, 시스템이 홈 타임라인의 질의 부하를 버텨내기 위해 고군분투해야 했고, 이제는 이 방식을 사용하지 않는다.

 

두번째 방법

각 수신 사용자 용 트윗 우편함처럼, 개별 사용자의 홈 타임라인 캐시를 유지한다. 사용자가 트윗을 작성하면, 해당 사용자를 팔로우하는 사람들을 모두 찾고, 팔로워 각자의 홈 타임라인 캐시에 새로운 트윗을 삽입한다. 그러면 홈 타임라인의 읽기 요청은, 요청 결과를 미리 계산했기 때문에 비용이 저렴하다. 평균적으로 트윗 게시 요청량이 홈 타임 라인 읽기 요청량에 비해 수백 배 적기 때문에 이 방식이 훨씬 잘 동작한다. ( 즉, 읽기와 쓰기 요청의 비율을 기준으로 판단한 것. ) 하지만 이 방식의 단점은, 이제 트윗을 작성하는 행위가 더 많은 부가 행위를 필요로 하게 됐다는 점이다. 평균적으로 트윗은 75명의 팔로워에게 전달되므로, 초 당 4,600개의 트윗이 쓰인다는 건, 345,000개의 쓰기를 요구하게 된다.

 

다만, 이것은 평균적인 경우를 의미하고, 소수의 특정 개별 사용자들은 팔로워가 3천만 명이 넘기도 하다. 이 경우에는 홈 타임 라인에 3천만 번의 쓰기 요청이 필요해질 수 있다는 의미가 된다. 트위터 측은 5초 이내에 팔로워에게 트윗을 전달하는 것을 목표로 하기 때문에, 이것만 가지고도 매우 중요한 도전 과제가 된다.

 

트위터 사례에서, 사용자 당 팔로워의 분포가 다른 것은, 앞서 얘기한 팬 아웃 부하를 결정하기 때문에, 확장성을 논의할 때 핵심 부하 매개변수가 된다. 트위터의 최종적인 방법은, 1번과 2번 방법을 혼합한 방식이다. 대부분의 사용자들은 트윗을 작성할 때 홈 타임 라인 안에서 펼쳐지지만, 팔로워 수가 무척이나 많은 경우에는 팬 아웃에서 제외된다. 즉, 사용자가 읽기 요청을 할 때에만 가져오도록 한다.

반응형