오늘은 책 1장에서 확성성을 설명하며 다룬 트위터(x) 사례를 집중 탐구하며 학습하고 실습을 해보겠습니다.
확장성에는 정답이 없고, 데이터의 특성(부하 매개변수)에 따라 설계가 완전히 달라져야 한다는 것을 보여줍니다.
1. 문제의 정의: 부하 매개변수(Load Parameter)
트위터의 주요 기능은 크게 두 가지입니다.
- 트윗 작성 (Post Tweet): 사용자가 글을 올린다. (평균 4.6k/sec, 피크 12k/sec)
- 홈 타임라인 조회 (Home Timeline): 팔로우한 사람들의 글을 모아서 본다. (300k/sec)
핵심 문제: 쓰기(Write)보다 읽기(Read) 요청량이 압도적으로(약 60배 이상) 많습니다. 이 비대칭성 때문에 아키텍처 고민이 시작됩니다.
2. 접근 방식 1: 관계형 데이터베이스 모델 (Pull 모델)
초창기 트위터가 사용한 방식이자, 우리가 일반적으로 생각하는 방식입니다.
- 동작 방식:
- 글을 쓰면 Tweets 테이블에 저장합니다. (단순 INSERT)
- 홈 타임라인을 볼 때, 내가 팔로우하는 사람 목록을 찾고 그들의 글을 시간순으로 정렬해서 가져옵니다.
- SQL로 표현하자면
SELECT tweets.*, users.*
FROM tweets
JOIN users ON tweets.sender_id = users.id
JOIN follows ON follows.followee_id = users.id
WHERE follows.follower_id = current_user
ORDER BY tweets.timestamp DESC;
- 장점: 글 쓰기 부하가 매우 적습니다.
- 치명적 단점: 읽기(홈 타임라인 조회)가 너무 느립니다. 팔로우하는 사람이 많아질수록 JOIN 비용이 기하급수적으로 늘어납니다. 초당 30만 건의 읽기 요청을 이 복잡한 쿼리로 처리하는 건 DB 입장에서 재앙입니다.
3. 접근 방식 2: 팬아웃 모델 (Push 모델)
트위터는 읽기 속도를 높이기 위해 아키텍처를 뒤집습니다. 읽을 때 고생하지 말고, 쓸 때 고생하자!는 전략입니다.
- 동작 방식 (쓰기 시점의 작업):
- 사용자가 트윗을 씁니다.
- 시스템은 그 사용자를 팔로우하는 모든 사람을 찾습니다.
- 각 팔로워의 '홈 타임라인 캐시(Redis 같은 In-memory DB)'에 해당 트윗 ID를 찔러 넣어줍니다(Push).
- 읽기 시점:
- 사용자는 그냥 자기 캐시(우편함)를 열어보기만 하면 됩니다. 복잡한 DB Join 없이 O(1)의 속도로 타임라인이 완성됩니다.
- 장점: 읽기 속도가 극단적으로 빠릅니다.
- 치명적 단점: 쓰기 부하가 폭발합니다(Fan-out).
- 팔로워가 100명인 사람이 글을 쓰면 100번의 쓰기 작업이 일어납니다.
- 만약 팔로워가 3,000만 명인 저스틴 비버(Justin Bieber)가 글을 쓰면? 단 한 번의 트윗으로 3,000만 건의 쓰기 요청이 찰나의 순간에 발생합니다. 이를 "핫스팟(Hot Spot)" 문제라고 합니다.

4. 트위터의 최종 해결책: 하이브리드(Hybrid)
"극단적인 두 가지 방식 중 하나만 고집하지 말라."
트위터는 두 방식을 섞었습니다.
- 일반 사용자 (팔로워 수가 적음): 접근 방식 2(Push/Fan-out)를 사용합니다. 쓰기 부하가 크지 않고 읽기는 빠르기 때문입니다.
- 유명인 (Celebrity, 팔로워가 매우 많음): 접근 방식 1(Pull)을 사용합니다. 저스틴 비버의 트윗은 팔로워들의 타임라인 캐시에 넣지 않습니다. 대신 팔로워가 타임라인을 조회할 때, "일반 사용자들의 캐시 데이터" + "유명인의 별도 트윗 데이터"를 그 시점에 합쳐서(Merge) 보여줍니다.
이것이 바로 시스템의 부하 특성(Load Parameter)에 맞춰 설계를 최적화한 사례입니다.
| 구분 | 일반 사용자 (그림의 모델) | 유명인 (예외 처리) |
| 방식 | Push (Fan-out) | Pull |
| 저장 위치 | 팔로워들의 Redis 캐시에 직접 꽂아줌 | 그냥 DB 테이블에만 저장함 |
| 쓰기 부하 | 높음 (팔로워 수만큼 복사해야 함) | 매우 낮음 (내 거 하나만 저장하면 됨) |
| 읽기 부하 | 매우 낮음 (캐시만 읽으면 됨) | 높음 (DB 조회 후 합치는 연산 필요) |
| 비유 | 우유 배달원이 집 앞 주머니에 우유를 넣어둠 (꺼내 먹기만 하면 됨) | 신문 가판대에 신문이 놓임 (내가 직접 가서 가져와야 함) |
일반 유저는 Push(캐시 미리 생성), 유명인은 Pull(조회 시 DB 조회)로 처리한다는 것을 알았습니다.
다음은 spring 환경에서 redis를 활용한 Push(fan out), Pull 실습 편을 기록하여 가져오겠습니다!
'Dev Book Review > 데이터 중심 애플리케이션 설계' 카테고리의 다른 글
| [DDIA Deep Dive] 트위터(X)는 어떻게 1억 명의 타임라인을 만들까? - 실습편 (1) | 2026.01.12 |
|---|