- Authors

- Name
- Youngju Kim
- @fjvbn20031
FANGシステムデザイン面接完全攻略ガイド
システムデザイン面接は、シニアエンジニアポジションにおける最も重要な関門の一つです。このガイドでは、FANG(Facebook/Meta、Apple、Amazon、Netflix、Google)の面接で頻出する問題を体系的に整理します。
1. システムデザイン面接フレームワーク:RESHADED
RESHADEDフレームワークは、45分の面接で体系的に回答するための構造です。
| ステップ | 内容 | 時間 |
|---|---|---|
| R - Requirements | 機能・非機能要件の明確化 | 5分 |
| E - Estimation | 規模推定(DAU、QPS、Storage) | 5分 |
| S - Storage | データモデルとDB選択 | 5分 |
| H - High-level design | 全体アーキテクチャの草案 | 10分 |
| A - APIs | APIエンドポイントの設計 | 5分 |
| D - Detailed design | 主要コンポーネントの詳細設計 | 10分 |
| E - Evaluation | トレードオフとボトルネック分析 | 3分 |
| D - Distinguishing features | 差別化要素の提案 | 2分 |
45分のタイム配分戦略
[0〜5分] 要件収集とスコープ定義
[5〜10分] キャパシティ推定(Back-of-envelope calculation)
[10〜20分] 高レベル設計(アーキテクチャ図)
[20〜35分] 主要コンポーネントの詳細設計
[35〜43分] トレードオフ議論と改善案の提示
[43〜45分] 面接官からの質問への対応
良い面接者 vs 悪い面接者
良い面接者:
- まず要件を明確にし、前提(assumption)を明示する
- 設計を進めながらトレードオフを自発的に言及する
- 数字をもとに設計上の意思決定を正当化する
- 「このアプローチにはX問題がありますが、Y理由で選びました」と明確に説明する
- 面接官をパートナーとして対話しながら進める
悪い面接者:
- 要件確認なしにいきなり設計を始める
- 1つのソリューションしか提示せず、代替案を検討しない
- 抽象的な回答に留まり、具体的な技術選定の根拠がない
- 長い沈黙や一人考え込みが続く
- 面接官のヒントを無視する
2. 主要概念クイック復習
2.1 垂直スケーリング vs 水平スケーリング
垂直スケーリング(スケールアップ):
- 単一サーバーのCPU・RAM・ディスクをアップグレード
- メリット:シンプル、データ整合性の維持が容易
- デメリット:ハードウェアの限界がある、SPOF(単一障害点)リスク
水平スケーリング(スケールアウト):
- サーバーインスタンスを追加して負荷分散
- メリット:理論上無限にスケール可能、障害回復が容易
- デメリット:状態共有の複雑性増加、ネットワークオーバーヘッド
垂直: [Server 4GB RAM] → [Server 32GB RAM]
水平: [Server] + [Server] + [Server] → ロードバランサーの前に配置
2.2 ロードバランサーアルゴリズム
| アルゴリズム | 説明 | ユースケース |
|---|---|---|
| ラウンドロビン | 順番にサーバーへ振り分け | サーバースペックが同一の場合 |
| 最小コネクション | 現在の接続数が最も少ないサーバーへ | リクエスト処理時間が多様な場合 |
| IPハッシュ | クライアントIPに基づき固定割り当て | セッション維持が必要な場合 |
| 重み付きラウンドロビン | サーバー性能に応じた重み付け | サーバースペックが異なる場合 |
2.3 CDN(コンテンツデリバリネットワーク)
CDNは静的コンテンツ(画像、JS、CSS、動画)を世界中のエッジサーバーにキャッシュし、ユーザーの近くから配信します。
ユーザー → [最寄りCDNエッジ] → (キャッシュヒット) → コンテンツ返却
→ (キャッシュミス) → [オリジンサーバー] → CDNキャッシュ後返却
Push CDN vs Pull CDN:
- Push CDN:コンテンツを事前にCDNへアップロード(大容量静的ファイルに適切)
- Pull CDN:初回リクエスト時にオリジンから取得してキャッシュ(動的コンテンツに適切)
2.4 キャッシング戦略
Cache-aside(遅延ロード):
1. アプリがキャッシュからデータを照会
2. キャッシュミス → DBから照会
3. DB結果をキャッシュに保存
4. 以降のリクエストはキャッシュヒット
Write-through:
1. アプリがキャッシュに書き込み
2. キャッシュが同期的にDBにも書き込み
→ データ整合性を保証、書き込み遅延が発生
Write-back(Write-behind):
1. アプリがキャッシュのみに書き込み
2. キャッシュが非同期的にDBに書き込み
→ 書き込み性能が優秀、キャッシュ障害時にデータ損失リスク
TTL(Time-To-Live): キャッシュの有効期限を設定。短すぎるとキャッシュ効率が低下し、長すぎると古いデータのリスクがあります。
2.5 データベース:SQL vs NoSQL の選択基準
SQLを選ぶ場合:
- ACIDトランザクションが必須(決済、在庫管理)
- データ構造が明確で変更が少ない
- 複雑なJOINクエリが必要
- 例:PostgreSQL、MySQL
NoSQLを選ぶ場合:
- スキーマが柔軟または頻繁に変更される
- 大規模サービスで水平スケーリングが必須
- 読み取りパフォーマンスが非常に重要
- 例:MongoDB(ドキュメント)、Cassandra(カラム)、Redis(KV)、Neo4j(グラフ)
2.6 CAPの定理の実務適用
CAPの定理:分散システムはConsistency(整合性)、Availability(可用性)、Partition tolerance(分断耐性)のうち同時に2つしか保証できません。
実務ではネットワーク分断(P)は避けられないため
CPシステム:銀行、株式取引(整合性優先)
APシステム:SNSフィード、ショッピングカート(可用性優先)
| システム | タイプ | 理由 |
|---|---|---|
| Zookeeper | CP | 分散ロック、設定管理 |
| Cassandra | AP | 常時書き込み可能、結果整合性 |
| HBase | CP | 強い整合性 |
| DynamoDB | AP(デフォルト) | 設定でCP可能 |
2.7 Consistent Hashing
通常のハッシュの問題:サーバー追加・削除時にほぼ全てのキーが再配置される。
Consistent Hashing:リング(輪)構造にサーバーとキーを配置。サーバー追加・削除時には一部のキーのみ再配置。
リング構造(0 〜 2^32 - 1):
0
/|\
/ | \
Server A Server B
\ | /
Server C
キーは時計回りに最も近いサーバーに割り当て
サーバー削除時はそのサーバーのキーのみが次のサーバーへ移動
仮想ノード: サーバーをリングに複数回配置することで均等分散を保証。
2.8 メッセージキュー
メッセージキューはサービス間の非同期通信を可能にし、疎結合(Loose Coupling)を実現します。
Kafka:
- 高スループット、永続性(ログ保持)、Consumer Groupサポート
- 用途:イベントストリーミング、ログ集約、リアルタイム分析
RabbitMQ:
- 複雑なルーティング、多様なメッセージパターンをサポート
- 用途:タスクキュー、通知システム
Producer → [Message Queue] → Consumer
メリット:
- 非同期処理でレスポンス時間を短縮
- トラフィックバッファリング(トラフィックスパイクを吸収)
- サービス間の結合度を低減
3. URL短縮サービス設計(TinyURL / bit.ly)
3.1 要件の明確化
機能要件:
- 長いURLを受け取り、短いURLを生成
- 短いURLへのアクセス時に元のURLへリダイレクト
- (オプション)カスタム短縮URLのサポート
- (オプション)URL有効期限の設定
非機能要件:
- DAU:1億(1億人の日次アクティブユーザー)
- 読み取り:書き込み比率 = 100:1
- 可用性:99.9% SLA
- リダイレクト遅延:100ms未満
3.2 キャパシティ推定
書き込みQPS:
- 1日の新規URL生成数:100M / 100 = 1M件/日
- 書き込みQPS:1,000,000 / 86,400 ≈ 12 QPS
読み取りQPS:
- 読み取り = 書き込み × 100 = 1,200 QPS
- ピーク:1,200 × 5 = 6,000 QPS
ストレージ:
- URLレコード1件 ≈ 500バイト
- 10年分のデータ:1M × 365 × 10 × 500バイト ≈ 1.8 TB
キャッシュ:
- 80/20の法則:上位20%のURLが80%のトラフィックを生成
- キャッシュサイズ:1,200 QPS × 86,400 × 0.2 × 500バイト ≈ 10 GB/日
3.3 API設計
POST /api/v1/urls
リクエスト:{ "long_url": "https://example.com/...", "expire_date": "2027-01-01" }
レスポンス:{ "short_url": "https://tinyurl.com/abc123" }
GET /{short_code}
レスポンス:301リダイレクト(永続)または302リダイレクト(一時)
→ 301:ブラウザがキャッシュ → サーバー負荷軽減(分析不可)
→ 302:常にサーバー経由 → クリック分析が可能
3.4 Base62エンコーディング
文字セット:[0-9, a-z, A-Z] = 62文字
6桁のBase62 = 62^6 = 568億通り
long_url → MD5/SHA-256 → 最初の7バイトを抽出 → Base62エンコード → 短縮コード
例:
"https://www.example.com/long-path"
→ MD5: "1a2b3c4d..."
→ 最初の7バイトの整数値 → Base62 → "aB3xY9z"
3.5 DBスキーマ設計
CREATE TABLE url_mappings (
id BIGINT PRIMARY KEY, -- Snowflake ID
short_code VARCHAR(8) UNIQUE NOT NULL,
long_url TEXT NOT NULL,
user_id BIGINT,
created_at TIMESTAMP DEFAULT NOW(),
expire_at TIMESTAMP,
click_count BIGINT DEFAULT 0
);
CREATE INDEX idx_short_code ON url_mappings(short_code);
DB選択: MySQL(シンプルなKVルックアップ、ACID不要)またはNoSQL(Cassandra)で水平スケーリング。
3.6 全体アーキテクチャ図
[クライアント]
|
v
[DNS] → [CDN](静的リソース)
|
v
[ロードバランサー]
|
+-----------+-----------+
v v v
[APIサーバー] [APIサーバー] [APIサーバー]
| |
v v
[Redisキャッシュ] [URL DB(マスター)]
(短縮URLキャッシュ) |
[URL DB(レプリカ)]
(読み取り専用)
書き込みフロー:
クライアント → LB → APIサーバー → DB保存 → Redisキャッシュ更新 → 短縮URL返却
読み取りフロー:
クライアント → LB → APIサーバー → Redisキャッシュ確認
→ キャッシュヒット:即座にリダイレクト
→ キャッシュミス:DB照会 → Redis保存 → リダイレクト
3.7 高可用性対応
- Redisクラスター: 短縮URLをRedisにキャッシュしてDB負荷を90%削減
- DBレプリケーション: マスター・スレーブ構造で読み書き分離
- レートリミット: 同一IPからの過剰な短縮URL生成をブロック
- 整合性保証: 重複短縮コード生成防止のためUUIDまたはSnowflake IDを使用
4. Twitter/Xフィードシステム設計
4.1 要件
- DAU:3億
- ツイート作成:500万件/日
- タイムラインロード:最新20件のツイート
- フォロワー数:平均300名、最大1億人(セレブ)
4.2 Fan-out戦略
Fan-out on write(プッシュモデル):
ツイート作成時:
1. ツイートをDBに保存
2. 作成者の全フォロワーIDを取得
3. 各フォロワーのタイムラインキャッシュ(Redis)にツイートIDを挿入
メリット:読み取りが非常に速い(Redisから直接取得)
デメリット:1億フォロワー = 1億回の書き込み → 書き込み遅延が大きい
Fan-out on read(プルモデル):
タイムライン取得時:
1. フォロー中アカウントのリストを取得
2. 各アカウントの最新ツイートを取得してマージ
3. 時系列でソートして返却
メリット:書き込みがシンプル
デメリット:読み取り時にフォロー数分のDBクエリ → 読み取り遅延が大きい
ハイブリッド戦略(実際のTwitter方式):
一般ユーザー(フォロワー < 10,000):Fan-out on write
セレブユーザー(フォロワー >= 10,000):Fan-out on read
タイムライン生成:
1. Redisから一般ユーザーのツイートIDを取得(Fan-out on write結果)
2. フォロー中セレブの最新ツイートを取得(Fan-out on read)
3. 両結果をマージしてソート
4.3 タイムラインキャッシング(Redis Sorted Set)
キー:timeline:{user_id}
値:Sorted Set(スコア = ツイートのタイムスタンプ、メンバー = tweet_id)
例:
ZADD timeline:123 1700000001 tweet:456
ZADD timeline:123 1700000002 tweet:789
ZREVRANGE timeline:123 0 19 # 最新20件取得
最大1,000件のツイートIDのみ保持(メモリ管理)
4.4 ツイートID生成(Snowflake ID)
Twitterが開発した64ビット分散ID生成方式:
64ビット構造:
[1ビット:符号] [41ビット:タイムスタンプ] [10ビット:マシンID] [12ビット:シーケンス]
メリット:
- 時系列ソートが可能(タイムスタンプ内包)
- 分散環境で重複なし
- 1秒あたり4,096個 × マシン数分の生成が可能
4.5 全体アーキテクチャ
[モバイル/Webクライアント]
|
[APIゲートウェイ]
|
+-----------+
| |
[ツイートサービス] [タイムラインサービス]
| |
[ツイートDB] [Redisタイムラインキャッシュ]
(Cassandra) |
[Fan-outサービス]
(Kafkaを消費して
フォロワーのタイムラインを更新)
|
[フォロワーグラフDB]
(フォロー関係を保存)
メディア処理:
[画像/動画アップロード] → [オブジェクトストレージ(S3)] → [CDN]
5. YouTube / Netflix 動画ストリーミング設計
5.1 要件
- DAU:20億(YouTube基準)
- 動画アップロード:500時間/分
- 同時ストリーミング:数億人
- 対応解像度:360p〜4K
5.2 動画アップロードパイプライン
[ユーザー]
|
v
[アップロードサービス] → S3にオリジナル保存
|
v
[メッセージキュー(Kafka)]
|
v
[トランスコーディングワーカー](並列処理)
├── 360p変換
├── 720p変換
├── 1080p変換
└── 4K変換
|
v
[CDNへ配信]
|
v
[メタデータDB更新] → ユーザーへ完了通知
トランスコーディング最適化:
- DAG(有向非巡回グラフ)ベースのタスク分割
- 動画をGOP(Group of Pictures)単位で分割して並列処理
- ウォーターマーク、サムネイル生成もパイプラインに含む
5.3 アダプティブビットレートストリーミング(ABR)
ユーザーのネットワーク状態に応じて自動的に画質切り替え:
[良好なネットワーク] → 1080p/4Kセグメントをリクエスト
[普通のネットワーク] → 720pセグメントをリクエスト
[低品質なネットワーク] → 360pセグメントをリクエスト
HLS(HTTP Live Streaming)方式:
- 動画を2〜10秒のセグメント(.tsファイル)に分割
- M3U8プレイリストファイルでセグメントリストを管理
- クライアントがバッファ状態を定期確認して画質を決定
5.4 CDN戦略
[オリジンサーバー] → [リージョナルCDN] → [エッジCDN] → [ユーザー]
人気動画:複数のエッジノードに事前キャッシュ(Push CDN)
非人気動画:初回リクエスト時にオリジンから取得してキャッシュ(Pull CDN)
Netflixの場合:ISPと直接協力してISP内部にキャッシュサーバーを配置
(OCA:Open Connect Appliance)
5.5 全体アーキテクチャ
アップロード経路:
[クリエイター] → [アップロードAPI] → [S3] → [Kafka] → [トランスコーディングクラスター]
|
[CDN配信]
視聴経路:
[ユーザー] → [APIゲートウェイ] → [動画サービス]
|
+-------------+-------------+
| | |
[メタデータ] [CDNストリーム] [推薦エンジン]
(MySQL) (HLS/DASH) (MLモデル)
6. チャットシステム設計(WhatsApp / Slack)
6.1 要件
- DAU:5億
- 1対1チャット、グループチャット(最大500名)
- メッセージ配信遅延:100ms未満
- 既読確認(WhatsAppの青いチェック)
- オンライン状態表示
6.2 リアルタイム通信プロトコル比較
| 方式 | 動作 | メリット | デメリット |
|---|---|---|---|
| ロングポーリング | レスポンスまで接続を維持 | 実装がシンプル | サーバーリソースの無駄 |
| SSE(サーバー送信イベント) | サーバー→クライアントの一方向 | 一方向ストリーミングに適切 | クライアント→サーバー不可 |
| WebSocket | 双方向全二重接続 | 低遅延、双方向 | 接続状態管理が複雑 |
チャットシステムの選択:WebSocket
クライアント ←→ WebSocket接続 ←→ チャットサーバー
(継続的な双方向通信)
6.3 メッセージ保存戦略
メッセージの特性:
- 書き込み頻度が非常に高い
- 読み取りは最近のメッセージが中心
- 削除はまれで変更なし
CassandraまたはHBaseを選ぶ理由:
Cassandraスキーマ:
パーティションキー:channel_id
クラスタリングキー:message_id(Snowflake、時系列ソート)
CREATE TABLE messages (
channel_id UUID,
message_id BIGINT, -- Snowflake ID(時刻内包)
sender_id UUID,
content TEXT,
created_at TIMESTAMP,
PRIMARY KEY (channel_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
→ チャンネルごとの最新メッセージ取得が非常に効率的
→ 水平スケーリングが容易
6.4 既読確認とオンライン状態
既読確認:
メッセージの状態:SENT → DELIVERED → READ
1. メッセージ送信 → 状態SENTでDBに保存
2. 受信者の端末に到達 → DELIVEREDに更新 → 送信者へ通知
3. 受信者がメッセージを確認 → READに更新 → 送信者へ通知
オンライン状態:
方法1:ハートビート(30秒ごとにサーバーへpingを送信)
- Redisに user:{id}:last_seen = タイムスタンプ を保存
- TTL 60秒設定 → 60秒以上pingがなければオフライン
方法2:WebSocket接続状態の追跡
- 接続時にオンライン、切断時にオフラインイベントを発生
- 友達へPub/Subで状態変更を伝播
6.5 グループチャットのメッセージ配信
小規模グループ(N < 100):
[送信者] → [チャットサーバー] → [各メンバーに直接WebSocket送信]
大規模グループ(N >= 100):
[送信者] → [チャットサーバー] → [Kafkaトピック:group:{id}]
|
[コンシューマークラスター]
|
[各メンバーの端末へプッシュ通知]
6.6 エンドツーエンド暗号化の概要
WhatsAppのSignalプロトコルベース:
1. 各端末で公開鍵・秘密鍵のペアを生成
2. サーバーには公開鍵のみを登録
3. 送信者:受信者の公開鍵でメッセージを暗号化
4. サーバー:暗号化済みメッセージのみを中継(内容を解読不可)
5. 受信者:自身の秘密鍵で復号化
7. 企業別システムデザイン出題傾向
| 問題 | 重要ポイント |
|---|---|
| 検索エンジン | クローラー、インデックス作成、PageRank、オートコンプリート |
| Webクローラー | URLフロンティア、重複排除、robots.txt、礼儀正しさ |
| Google Maps | マップタイル、経路探索(Dijkstra)、ETA予測 |
| Google Drive | ファイルのアップロード/ダウンロード、リアルタイム共同編集、バージョン管理 |
Meta(Facebook/Instagram)
| 問題 | 重要ポイント |
|---|---|
| ニュースフィード | Fan-out戦略、エッジランキングアルゴリズム |
| 写真アップロード、フォローグラフ、タイムライン | |
| Facebookメッセージ | リアルタイムチャット、メッセージ同期 |
| フレンド推薦 | グラフDB、共通友人の計算 |
Amazon
| 問題 | 重要ポイント |
|---|---|
| Eコマースカート | セッションストレージ、在庫管理、決済処理 |
| 推薦システム | 協調フィルタリング、リアルタイム vs バッチ |
| Amazon S3 | オブジェクトストレージ、11-9sの耐久性、マルチパートアップロード |
| 注文処理システム | 分散トランザクション、Sagaパターン |
Netflix
| 問題 | 重要ポイント |
|---|---|
| 動画ストリーミング | ABR、CDN、トランスコーディング |
| 推薦システム | A/Bテスト、パーソナライズされたML |
| APIゲートウェイ | レートリミット、サーキットブレーカー |
| 通知システム | リアルタイム通知システム |
8. 主要設計パターンまとめ
データベースパターン
読み取りスケーリング:マスター・スレーブレプリケーション + 読み取り専用レプリカ
書き込みスケーリング:シャーディング(Consistent Hashing)
キャッシング:Redis(インメモリ) → DB負荷を削減
検索:Elasticsearch → 全文検索
時系列データ:InfluxDB / TimescaleDB
グラフデータ:Neo4j / Amazon Neptune
非同期処理パターン
タスクキュー:重い処理(トランスコーディング、メール送信)を非同期処理
イベントストリーミング:Kafka → サービス間のイベント伝播
CQRS:読み取り/書き込みモデルを分離してパフォーマンスを最適化
Sagaパターン:分散トランザクションの処理
クイズ
クイズ1:URL短縮サービスにおける301リダイレクトと302リダイレクトの違いは何ですか?また、それぞれどのような場合に使うべきですか?
答え:301は永続リダイレクト(Moved Permanently)、302は一時リダイレクト(Found/Temporary Redirect)です。
解説:301を使用すると、ブラウザがリダイレクト結果をキャッシュするため、次回以降のリクエストはサーバーを経由しません。サーバー負荷は減りますが、クリック分析ができません。302を使用すると、常にサーバーを経由するため、クリック数の追跡、A/Bテスト、URL変更が自由にできます。URL短縮サービスが広告や分析データを収集する場合は302を、サーバー負荷の最小化が優先の場合は301を選択します。
クイズ2:Twitterのfan-out on write方式で、フォロワーが1億人いるセレブアカウントがツイートを投稿するとどのような問題が起きますか?
答え:「ホットスポット」または「セレブ問題」と呼ばれる書き込みストーム(write storm)が発生します。
解説:1億人のフォロワーにfan-out on writeでツイートIDを配信するには、Redisに1億回の書き込み操作が必要です。これは数分の遅延と膨大なリソース消費を引き起こします。実際のTwitterはフォロワーの閾値(約10,000人)を基準に、セレブアカウントにはfan-out on read方式を適用しています。ユーザーがタイムラインを取得する際に、フォロー中のセレブの最新ツイートを別途取得してマージします。
クイズ3:Cassandraがチャットメッセージの保存に適している理由を3つ説明してください。
答え:高い書き込みスループット、時系列ソートのサポート、水平スケーラビリティです。
解説:(1) CassandraのLSM-Treeは、ディスクのランダムI/Oなしにシーケンシャル書き込みをサポートし、1秒間に数十万件のメッセージ挿入が可能です。(2) Snowflake IDをClustering Keyとして使用すると、タイムスタンプベースのソートが自動的に行われ、最新メッセージの取得が効率的です。(3) ノードを追加するだけで線形なパフォーマンス拡張が可能で、自動的にデータが再分配されます。一方、MySQLは大容量のメッセージテーブルではインデックスオーバーヘッドと垂直スケーリングの限界が問題となります。
クイズ4:CAPの定理において、銀行システムとSNSのいいね数はそれぞれどのタイプ(CP/AP)であるべきか、その理由も説明してください。
答え:銀行システムはCP、SNSのいいね数はAPです。
解説:銀行口座の残高は正確性が絶対的に重要です。ネットワーク分断が発生した場合、誤った残高情報を提供するよりも一時的なサービス停止の方がましです。そのためCPを選択し、強い整合性を保証します。一方、SNSのいいね数は1,234,567と1,234,570の違いがユーザー体験にほぼ影響しません。ネットワーク分断時もサービスが継続して動作すること(可用性)の方が重要なため、APを選択して結果整合性を許容します。
クイズ5:動画ストリーミングサービスにおいてアダプティブビットレートストリーミング(ABR)が必要な理由と動作原理を説明してください。
答え:多様なネットワーク環境のユーザーに途切れのないストリーミングを提供するためです。
解説:モバイルユーザーは4GからWiFiへ、地下から地上へ移動しながらネットワーク帯域幅が急激に変化します。固定画質ストリーミングではネットワークが悪化するとバッファリングが発生します。ABRは動画を2〜10秒のセグメントに分割し、複数の画質(360p〜4K)で事前エンコードします。クライアントプレイヤーはバッファレベルとダウンロード速度を監視して、次のセグメントの画質を動的に選択します。ネットワークが悪化すると低画質に切り替えてバッファリングなしの再生を維持します。