SYSTEM DESIGN GUIDE — PART 7 / 7
- 面接を突破する
- Google の面接プロセス全体像
- 44.1 典型的なプロセス
- 44.2 レベルごとの期待値(システムデザイン)
- 44.3 評価され得る観点
- 44.4 行動面接(Googleyness)の準備
- 44.5 準備スケジュール(面接まで 3 ヶ月ある場合)
- システムデザイン面接の進め方(45 分の台本)
- 45.1 全体の時間配分
- 45.2 ステップ①:要件の明確化(5〜8 分)
- 45.3 ステップ②:規模の見積もり(3〜5 分)
- 45.4 ステップ③:API 設計(3〜5 分)
- 45.5 ステップ④:データモデル(3〜5 分)
- 45.6 ステップ⑤:ハイレベル設計(8〜10 分)
- 45.7 ステップ⑥:深掘り(10〜15 分)— ここで差がつく
- 45.8 ステップ⑦:まとめ(5 分)
- 45.9 話し方の実践テクニック
- 45.10 45 分のチェックリスト(暗記推奨)
- 評価シグナルと減点ポイント
- 46.1 「Strong Hire」の答案に共通すること
- 46.2 減点される答案
- 46.3 深掘りに耐えるための「3 段掘り」練習法
- 46.4 よく出る「意地悪な質問」への回答例
- 演習:設計問題 19 本ノック
- 1-1. 要件の確認
- 1-2. 見積もり
- 1-3. API
- 1-4. 短縮キーの生成(この問題の核心)
- 1-5. アーキテクチャ
- 1-6. 深掘り
- 1-7. まとめの言い方
- 2-1. 要件
- 2-2. 見積もり(第 3 章の再掲)
- 2-3. 核心:ファンアウト戦略
- 2-4. データモデル
- 2-5. アーキテクチャ
- 2-6. 深掘り
- 2-7. まとめ
- 3-1. 要件
- 3-2. 見積もり
- 3-3. 通信方式
- 3-4. アーキテクチャ
- 3-5. データモデル(Cassandra)
- 3-6. 深掘り
- 3-7. まとめ
- 4-1. 要件
- 4-2. 見積もり
- 4-3. アップロードとトランスコードのパイプライン
- 4-4. 配信(ABR ストリーミング)
- 4-5. データモデル
- 4-6. 深掘り
- 4-7. まとめ
- 5-1. 要件
- 5-2. 核心:チャンク化と重複排除
- 5-3. データモデル
- 5-4. アーキテクチャ
- 5-5. 深掘り
- 5-6. まとめ
- 6-1. 要件
- 6-2. 見積もり
- 6-3. アーキテクチャ
- 6-4. 一貫性が必要な箇所を切り分ける(この問題の核心)
- 6-5. データモデル
- 6-6. 深掘り
- 6-7. まとめ
- 7-1. 要件
- 7-2. 見積もり
- 7-3. アーキテクチャ
- 7-4. 核心的な設計要素
- 7-5. 深掘り
- 7-6. まとめ
- 8-1. 要件
- 8-2. 見積もり
- 8-3. データ構造:Trie(トライ木)
- 8-4. アーキテクチャ
- 8-5. 深掘り
- 8-6. まとめ
- 9-1. 要件
- 9-2. アーキテクチャ
- 9-3. データモデル
- 9-4. 深掘り
- 9-5. まとめ
- 10-1. 要件
- 10-2. 設計
- 10-3. 深掘り
- 11-1. 要件
- 11-2. 核心的な設計原則
- 11-3. アーキテクチャ
- 11-4. データモデル
- 11-5. 深掘り
- 11-6. まとめ
- 12-1. 要件
- 12-2. 設計の核心
- 12-3. アーキテクチャ
- 12-4. 深掘り
- 13-1. 要件
- 13-2. 設計
- 13-3. 深掘り
- 14-1. 要件
- 14-2. 見積もり
- 14-3. 時系列データの圧縮(Facebook の Gorilla 方式)
- 14-4. アーキテクチャ
- 14-5. 深掘り
- 15-1. 要件
- 15-2. 見積もり
- 15-3. 配信パス(低レイテンシが命)
- 15-4. 集計パス(正確性が命)
- 15-5. 深掘り
- 16-1. 要件
- 16-2. 設計
- 16-3. 発売開始の殺到(サンダリングハード)への対策
- 16-4. 深掘り
- 16-5. まとめ
- 17-1. 要件
- 17-2. 見積もり
- 17-3. API
- 17-4. アーキテクチャ
- 17-5. 深掘り
- 17-6. まとめ
- 18-1. 要件
- 18-2. 見積もり
- 18-3. 3 段階推薦パイプライン
- 18-4. データモデルと特徴量ストア
- 18-5. 深掘り
- 18-6. まとめ
- 19-1. 要件
- 19-2. 見積もり
- 19-3. アーキテクチャ(Prefill / Decode 分離 + PagedAttention)
- 19-4. 核心技術
- 19-5. 深掘り
- 19-6. まとめ
- 19-7. 追加課題:業務ツールを操作する AI エージェント
- 演習問題の総まとめ:頻出パターン早見表
- Google の実システムを読む
- 48.1 ストレージ系
- 48.2 計算・データ処理系
- 48.3 ネットワーク・分散基盤
- 48.4 面接で語れる「思想」
- コーディング面接・OS・並行処理の最低限
- 49.0 この章の位置付け(コーディング対策の境界)
- 49.1 計算量
- 49.2 必修アルゴリズムパターン(LeetCode 頻出)
- 49.3 OS の基礎(システムデザインに直結する部分だけ)
- 49.4 並行処理
- 49.5 実装・検証の仕上げ
- 12 週間の学習ロードマップ
- 50.1 全体計画
- 50.2 毎週の固定メニュー
- 50.3 学習効果を最大化する 5 つのコツ
- 50.4 参考文献(さらに深く学ぶために)
- 50.5 面接前日・当日
- 用語集
- A. 基礎・性能
- B. ネットワーク
- C. データ
- D. 部品
- E. 信頼性・運用
- F. アーキテクチャ
- G. AI / ML / エージェント
- この本で伝えたかったこと
- 最後に
面接を突破する
45分の台本、評価シグナル、設計問題19本、Googleの実システム、コーディング・OS、12週間のロードマップ、用語集。ここまでの知識を、面接の場で説明できる設計力へ変える最終Partです。
- ① 導入と土台
- ② ネットワークと通信
- ③ データを保存する
- ④ システムの部品箱
- ⑤ 壊れないシステム
- ⑥ アーキテクチャの型
- ⑦ 面接を突破する
Google の面接プロセス全体像
選考の全体像・レベル別期待値・行動面接までを把握する
🎯 この章のゴール: 何が評価され、どのレベルを求められているかを知る。
⚠️ 注意: 採用プロセスの細部は時期・チーム・国によって変わります。 以下は一例です。ラウンド数、試験形式、レベル表記、委員会・チームマッチの有無は募集職種ごとに異なります。 公式の採用案内と最新のリクルーターの説明を正としてください。
44.1 典型的なプロセス
① 書類 / リファラル
↓
② リクルーターとの面談(15〜30 分)
経歴確認、希望レベル、タイムラインの説明
↓
③ 技術電話スクリーニング(45 分 × 1〜2 回)
共有エディタでのコーディング 1〜2 問
↓
④ オンサイト(バーチャル): ラウンド数・時間は職種と時期で異なる
・コーディング、システムデザイン、行動面接などから構成される場合がある
※ レベルによって重点は変わるが、L4以上で必ず同じ形式になるとは限らない
↓
⑤ 採用に関するレビュー
面接フィードバックを複数のレビュー段階で審査する場合がある。具体的な権限と手順は公開情報・職種で確認する
↓
⑥ チームマッチング
↓
⑦ 上級レビュー・オファー
📐 面接では、仮定、推論、トレードオフ、コードや設計の根拠を説明できることが重要です。 黙って考え続けず、考えていることを短く共有します。ただし、発話量そのものが合格条件ではなく、 正確な対話、質問への応答、指摘を受けた修正が評価されます。
44.2 レベルごとの期待値(システムデザイン)
| レベル | 呼称 | システムデザインで求められること |
|---|---|---|
| L3 | SWE II(新卒) | システムデザインは軽いか無いことが多い。基礎理解 |
| L4 | SWE III | 既知のコンポーネントを組み合わせて、要件を満たす設計ができる。トレードオフを 1〜2 個説明できる |
| L5 | Senior SWE | 曖昧な要件を自分で構造化し、規模の見積もりから設計を導ける。障害・運用・進化まで語れる |
| L6 | Staff SWE | 複数チームにまたがる設計、組織・移行戦略・長期的な技術投資まで |
🎤 L4 と L5 を分ける最大の違い:
- L4: 「キャッシュを入れます」
- L5: 「読み:書きが 100:1 なのでキャッシュを入れます。ヒット率は 90% を想定し、 ミス時の DB 負荷は 2,000 QPS。キャッシュ障害時は DB が耐えられないので、 ミス時のレート制限とローカルキャッシュの二層構成にします。 無効化は TTL + 更新時削除で、スタンピード対策にジッタを入れます。」
同じ「キャッシュを入れる」でも、数字・障害・運用まで語れるかが分岐点です。
44.3 評価され得る観点
公開資料や職種によって表現は異なります。次は準備の観点として整理したものです。
| 軸 | 内容 |
|---|---|
| GCA (General Cognitive Ability) | 曖昧な問題を構造化し、論理的に解を導けるか。思考プロセスが見られる |
| RRK (Role-Related Knowledge) | 職務に必要な技術知識 |
| Leadership | 主体性、周囲を巻き込む力(emergent leadership) |
| Googleyness | 曖昧さへの耐性、協調性、ユーザー志向、謙虚さと自信のバランス |
44.4 行動面接(Googleyness)の準備
STAR 法で答えます。
Situation: 状況(背景を 1〜2 文で簡潔に)
Task : あなたの課題・役割
Action : **あなたが**何をしたか(「チームが」ではなく「私が」)
Result : 結果。**数字で**(レイテンシ 40% 改善、障害 0 件、リリース 2 週間短縮)
準備しておくべきエピソード(各 2 つずつ):
□ 技術的に最も難しかった問題と、どう解決したか
□ 意見が対立したとき、どう合意形成したか
□ 失敗した経験と、そこから学んだこと(**必須。正直に話す**)
□ 期限が厳しい中で優先順位をつけた経験
□ 曖昧な要件から仕事を進めた経験
□ 誰かを助けた/メンタリングした経験
□ フィードバックを受けて自分を変えた経験
□ 自分の担当外の問題を拾って解決した経験
⚠️ 失敗談で「実は成功でした」というオチにしないこと。 本当の失敗と、そこから何を変えたかを話す方が高く評価されます。
44.5 準備スケジュール(面接まで 3 ヶ月ある場合)
Week 1-4 : 本書の第 1〜4 部を通読 + LeetCode の基礎(配列・文字列・木・グラフ)
Week 5-8 : 本書の第 5〜6 部 + 演習問題を紙で解く + LeetCode Medium 中心
Week 9-10: 模擬面接(人と一緒に、45 分計測)× 6 回以上
Week 11 : 弱点の補強、行動面接の準備
Week 12 : 総復習、コンディション調整
📐 模擬面接を必ず組み込んでください。 「頭で分かっている」と「45 分間、声に出して図を描きながら説明できる」の間には 非常に大きな差があります。1 人でやる場合も、声に出して録音すること。
システムデザイン面接の進め方(45 分の台本)
45分を7ステップに分解し、話す順番を体に入れる
🎯 この章のゴール: 時間配分と話す順番を体に染み込ませる。
45.1 全体の時間配分
┌────────────────────────────────────────────────┐
│ ① 要件の明確化 5〜8 分 ★ 最重要 │
│ ② 規模の見積もり 3〜5 分 │
│ ③ API 設計 3〜5 分 │
│ ④ データモデル 3〜5 分 │
│ ⑤ ハイレベル設計 8〜10 分 ★ 中核 │
│ ⑥ 深掘り(1〜2 箇所) 10〜15 分 ★ 差がつく │
│ ⑦ 障害・運用・まとめ 5 分 │
└────────────────────────────────────────────────┘
⚠️ 最も多い失敗は ① を飛ばして ⑤ から始めることです。 要件が曖昧なまま描いた設計は、必ず途中で崩れます。
45.2 ステップ①:要件の明確化(5〜8 分)
質問の例(問題に応じて取捨選択する)
【機能要件】
・このシステムの主要なユースケースは何ですか? 誰が使いますか?
・今回スコープに含める機能はどれですか?(3〜5 個に絞る)
・逆に、今回は考えなくてよいものは何ですか?
【非機能要件】
・想定ユーザー数(DAU)はどのくらいですか?
・読み取りと書き込みの比率は?
・レイテンシの要件は?(p99 で何 ms 以内)
・可用性の要件は?(99.9%? 99.99%?)
・一貫性の要件は?(結果整合で許されますか?)
・データの保持期間は?
・グローバル展開しますか? 単一リージョンでよいですか?
🎤 言い方の例:
「Twitter のようなサービスということですが、今回は ① ツイートの投稿、② ホームタイムラインの表示、③ フォロー機能 の 3 つに絞ってよいでしょうか。 検索、トレンド、DM、広告は今回はスコープ外とします。 規模は DAU 2 億人、1 人 1 日 2 投稿・200 回閲覧と仮定します。この前提で進めてよいですか?」
📐 ここで面接官が仮定を修正してくれます。それが重要な情報です。
45.3 ステップ②:規模の見積もり(3〜5 分)
第 3 章の手順で。必ず声に出しながら。
・QPS(読み・書き、平均・ピーク)
・ストレージ(1 日・1 年・5 年、レプリカ込み)
・帯域(特に画像・動画があるなら)
・キャッシュに必要なメモリ
📐 出した数字は必ず設計に反映する:
「読み取りが書き込みの 100 倍なので、読み取り最適化が設計の中心になります。」 「5 年で 2 PB なので、単一 DB では不可能です。シャーディング前提で進めます。」
45.4 ステップ③:API 設計(3〜5 分)
主要な 3〜5 個だけ。要件と面接官の関心に応じて、APIを先に細かく書かず設計の核心へ進んでも構いません。
POST /v1/tweets {content, media_ids} → {tweet_id}
GET /v1/timeline?cursor=&limit=20 → {items[], next_cursor}
POST /v1/users/{id}/follow
📐 言及すると加点されること: ページネーション方式(カーソル)、 冪等性(重複投稿の防止)、認証方式。
45.5 ステップ④:データモデル(3〜5 分)
users(user_id PK, name, ...)
tweets(tweet_id PK, user_id, content, created_at, media_url)
follows(follower_id, followee_id, created_at) PK(follower_id, followee_id)
timeline(user_id, tweet_id, created_at) ← 事前計算したタイムライン
📐 必ず言うこと: どの DB を選ぶか、なぜそれか、シャードキーは何か。
「tweets は user_id でシャーディングします。follows は 『フォロワー一覧』と『フォロー中一覧』の両方が必要なので、 双方向のテーブルを持つか、
(follower_id, followee_id)と(followee_id, follower_id)の 2 つのインデックスを持ちます。」
45.6 ステップ⑤:ハイレベル設計(8〜10 分)
箱と矢印を描き、データの流れを説明します。
[Client] → [CDN] → [LB] → [API Gateway] → [Service群]
├→ [Cache]
├→ [DB (shard)]
└→ [Queue] → [Worker]
🎤 説明の順番: 書き込みパス → 読み取りパスの順に、 「1 つのリクエストが辿る道」を追いかけて説明します。
「まずツイート投稿のパスを追います。クライアントが POST すると…(順に)…
次に、タイムライン取得のパスです。…」
⚠️ 箱を描くだけで説明しないのは最悪です。必ず「なぜこの箱が要るのか」を言う。
45.7 ステップ⑥:深掘り(10〜15 分)— ここで差がつく
面接官は必ずどこかを掘ってきます。自分から掘る候補を提示するのも good です。
🎤 「ここからは、タイムライン生成のファンアウト戦略が最も重要な設計判断なので、 そこを詳しく議論してもよろしいでしょうか?」
よく掘られるポイント(準備しておく):
□ ボトルネックはどこか
□ そのキャッシュが落ちたら?
□ 10 倍の負荷が来たら?
□ ホットスポット(有名人・人気商品)はどう扱う?
□ データの一貫性はどう保証する?
□ 重複したリクエストが来たら?(冪等性)
□ そのシャードキーだと、こういうクエリはどうなる?
□ デプロイやスキーマ変更はどうする?
□ 監視は何を見る?
45.8 ステップ⑦:まとめ(5 分)
・設計の要点を 30 秒で再掲
・**既知の弱点と、その改善案**を自分から述べる ← ここが非常に効く
・今回スコープ外にしたものと、それを入れるならどうするか
🎤 締めの言い方:
「まとめます。読み取りが支配的なので、書き込み時にタイムラインを事前計算し、 読み取りをキャッシュから O(1) で返す設計にしました。 フォロワーの多いアカウントだけは pull 型にするハイブリッドです。
現状の弱点は 2 つあります。① 事前計算のためストレージが増えること、 ② ファンアウトの遅延で、投稿が数秒後に見える可能性があることです。 ①は古いタイムラインを TTL で削り、非アクティブユーザーには事前計算しないことで 削減できます。②は自分の投稿だけクライアント側で即時挿入すれば体感を保てます。
時間があれば、次は検索とトレンドの設計を議論したいところです。」
45.9 話し方の実践テクニック
✅ やるべきこと
- 常に声に出して考える(沈黙は評価不能。「今、2 つの選択肢を比べています」でもよい)
- 仮定を宣言する(「ここは X と仮定して進めます」)
- 選択の理由を必ず添える(「なぜなら〜」を口癖にする)
- 面接官のヒントを拾う(「そこは良い指摘です、確かに〜」)
- 知らないことは正直に(「その製品は使ったことがありませんが、 同種の問題は〜という仕組みで解けると思います」← これは減点になりません)
- 図を整理して描く(左から右へ、上から下へ。乱雑な図は思考が乱雑に見える)
❌ やってはいけないこと
- 要件を聞かずに描き始める
- バズワードの羅列(「Kubernetes と Kafka と GraphQL を使います」)
- 1 つの話題に 20 分使う(時間配分を意識する)
- 面接官の指摘を無視する / 逆に何でも即座に迎合する
- 知らないことを知っているふりをする(最も危険。掘られて崩壊する)
- 完璧を目指して沈黙する
45.10 45 分のチェックリスト(暗記推奨)
□ 機能要件を 3〜5 個に絞ったか
□ 非機能要件(規模・レイテンシ・可用性・一貫性)を聞いたか
□ QPS とストレージを計算し、それを設計に反映したか
□ API を 3〜5 個定義したか
□ データモデルと DB 選定の理由を述べたか
□ シャードキーを述べたか
□ ハイレベル図を描き、書き込み/読み取りパスを説明したか
□ キャッシュ戦略(どこに何を、無効化方法)を述べたか
□ ボトルネックを特定し、対処を述べたか
□ 障害時の挙動(degrade / フェイルオーバー)を述べたか
□ 監視項目を述べたか
□ 弱点と今後の改善を自分から述べたか
評価シグナルと減点ポイント
Strong Hire のシグナルと、典型的な減点を知る
「知っている部品の数」よりも、要件から設計判断を導き、壊れ方まで説明できるかが重要です。
46.1 「Strong Hire」の答案に共通すること
- 数字が設計を駆動している 「1 日 400 GB なので〜」「p99 が 200 ms 必要なので〜」
- 選択肢を挙げ、比較し、選び、条件が変われば別を選ぶと言える
- 失敗を前提に設計している 「これが落ちたら〜に degrade します」
- 深掘りに耐える 3 段階掘られても答えられる(例: キャッシュ → 無効化 → 並行更新時の順序 → 分散ロック → フェンシング)
- スコープ管理ができている 「それは重要ですが、まず主要パスを固めてから戻ってもよいですか」
- 面接官と協調している(議論になっている)
46.2 減点される答案
| 減点行動 | なぜダメか | 代わりにどうする |
|---|---|---|
| 要件を聞かずに描く | 何を解くか分かっていない | 最初の 5 分は必ず質問 |
| 「大量のリクエスト」 | 定量化できていない | 「ピーク 5 万 QPS」 |
| 最初から複雑な構成 | 過剰設計の傾向を示す | 単純な構成 → 制約に応じて拡張 |
| 「Redis を使います」だけ | 理由がない | 「〜なので Redis。ただし〜が欠点」 |
| 曖昧な受け答え | 知識の深さが不明 | 知らないなら知らないと言い、推論を述べる |
| 面接官の指摘に反発 | 協調性の懸念 | 「なるほど、その場合は〜」 |
| 何でも「はい直します」 | 判断力がない | 「その通りです。ただ〜という制約もあるので〜」 |
| 時間切れで未完成 | 優先順位付けができない | 常に時計を見て、粒度を調整 |
46.3 深掘りに耐えるための「3 段掘り」練習法
各トピックについて、「なぜ?」を 3 回自問してください。
例1: キャッシュ
1段: なぜキャッシュ? → 読み:書き = 100:1、ヒット率を測って DB 負荷の削減幅を見積もる
2段: どう無効化? → 更新時に削除 + TTL(ジッタ付き)
3段: 並行更新で古い値が残らない? → 更新でなく削除にする理由、
それでも起きる競合には遅延二重削除 or バージョン付きキー
例2: シャーディング
1段: なぜシャード? → 5 年で 2 PB、単一ノードに載らない
2段: シャードキーは? → user_id。理由は分布とクエリパターン
3段: 有名人で偏る場合は? → そのユーザーだけ pull 型に切替、
またはサブシャード化(user_id + bucket)
例3: キュー
1段: なぜキュー? → 重い処理を同期パスから外す
2段: 重複配信は? → at-least-once なので冪等キーで排除
3段: 順序は? → パーティション内のみ保証。同一ユーザーは同じキーへ
📐 本書の各章の「一問一答」は、この 2〜3 段目に相当します。
46.4 よく出る「意地悪な質問」への回答例
| 質問 | 良い回答の方向 |
|---|---|
| 「その DB が落ちたら?」 | フェイルオーバーの仕組み、RPO/RTO、その間の degrade(読み取り専用モード) |
| 「10 倍のトラフィックが来たら?」 | どこが最初に壊れるかを特定 → その対処。「まずキャッシュのヒット率、次に DB の接続数」 |
| 「もっと安くできる?」 | ストレージ階層、事前計算 vs オンデマンド、サンプリング、CDN |
| 「もっとシンプルにできる?」 | 「はい、この規模なら〜は不要です。要件が〜になったら必要になります」 |
| 「なぜ X ではなく Y?」 | 両者の軸を挙げて比較。「要件が〜なら X が正解です」と条件も述べる |
| 「その計算、間違ってない?」 | 落ち着いて再計算。間違っていたら素直に訂正(訂正できることも評価対象) |
| 「私はこう思うんだけど」 | まず理解し、良ければ取り入れ、懸念があれば根拠と共に述べる |
🎤 困ったときの万能フレーズ:
「少し考える時間をいただけますか。…… 整理すると、この問題は〜と〜のトレードオフだと思います。 私は〜の理由で前者を選びますが、〜という制約があるなら後者が適切です。」
演習:設計問題 19 本ノック
19の代表問題を、45分の型で実際に解く
使い方: まず自分で 45 分かけて紙に設計してから、解答を読んでください。 読むだけでは絶対に身につきません。手を動かした量がそのまま得点になります。
各問題は次の形式です:
要件の確認 → 見積もり → API → データモデル → 設計 → 深掘り → まとめ
各問題は 要件 → 見積もり → API → データモデル → 設計 → 深掘り → まとめ の同じ型で整理しています。
Q 01URL 短縮サービス(bit.ly)★☆☆(最初に練習すべき問題)解答を開く
難易度: ★☆☆(最初に練習すべき問題)
1-1. 要件の確認
機能要件:
・長い URL を短い URL に変換する
・短い URL にアクセスすると元 URL へリダイレクトする
・任意のカスタムエイリアス(optional)
・有効期限(optional)
機能外:
・分析(クリック数)は今回は簡易的に
非機能:
・可用性が最重要(リダイレクトが落ちると全リンクが死ぬ)
・リダイレクトのレイテンシ p99 < 50 ms
・短縮 URL は推測されにくい方が望ましい
・読み:書き = 100:1
1-2. 見積もり
書き込み: 1 億 URL/月 = 1億 / (30 × 10^5) ≒ 33 QPS(ピーク 100 QPS)
読み取り: 33 × 100 = 3,300 QPS(ピーク 1 万 QPS)
ストレージ: 1 レコード ≒ 500 B(短縮キー、元URL、user_id、作成日、期限)
5 年分 = 1 億 × 12 × 5 = 60 億件 × 500 B = 3 TB
→ **単一ノードの容量だけでなく、IOPS、ワーキングセット、復旧時間を評価し、レプリカ + 将来のシャーディングを考慮**
キャッシュ: 全レコードの割合ではなく、アクセス分布、TTL、キャッシュ項目サイズ、ヒット率目標から容量を決める
1-3. API
POST /v1/urls {long_url, custom_alias?, expires_at?} → {short_url}
GET /{key} → 301/302 リダイレクト
📐 301 と 302 の選択(頻出):
- 301 (Permanent): ブラウザがキャッシュ → サーバー負荷が減るが、クリック計測ができない
- 302 (Found): 毎回サーバーに来る → 計測できるが負荷が高い → 分析が要るなら 302、負荷削減優先なら 301。実務では 302 + 短い Cache-Control が多い。
1-4. 短縮キーの生成(この問題の核心)
| 方式 | 説明 | 評価 |
|---|---|---|
| A. ハッシュ + 先頭 7 文字 | MD5(url) を Base62 化して先頭 7 文字 | 衝突チェックが必要(DB 参照)。同じ URL が同じキーになる |
| B. 連番 → Base62 | 分散カウンタで採番し Base62 変換 | 衝突なし・短い。ただし推測可能(+1 すれば他人の URL) |
| C. 事前生成キー(KGS) | あらかじめランダムキーを大量生成し、一意制約で確保しておく | ⭐ 配布時の衝突を避けられ、推測困難・高速。生成時の一意性確認は必要 |
📐 推奨: C(Key Generation Service)
オフラインで 62^7 空間からキーを生成し、生成時に一意性を確認して reserved として保存
→ 各アプリノードが 1000 個ずつアトミックに claim してメモリに保持
→ 採番はメモリ内で完結(DBアクセスなし)
→ ノード再起動で未使用キーが失われても、再利用・回収できる状態を持つ
📐 長さの計算: 62^7 = 約 3.52 兆。60 億件使っても占有率は約 0.17% ですが、
ランダム抽出の誕生日問題では衝突確率はほぼ 1 です。期待される衝突ペア数は概算で
m(m-1)/(2N) ≒ 510 万。一意性は空間の広さではなく、生成・保存時に保証します。
1-5. アーキテクチャ
[Client]
│ GET /aBc1De2
▼
[CDN / エッジ](人気リンクはエッジでキャッシュ)
▼
[LB] → [リダイレクトサービス(ステートレス、多数)]
│ ①キャッシュを見る
▼
[Redis クラスタ] key → long_url(LRU、TTL 24h)
│ ミス
▼
[KV ストア / DynamoDB or Cassandra] ← シャードキー = short_key
[作成サービス] → [KGS(事前生成キープール)] → [KV ストア]
↓
[クリックイベント] → Kafka → 分析基盤(非同期、リダイレクトを遅らせない)
1-6. 深掘り
| 論点 | 回答 |
|---|---|
| なぜ RDB でなく KV? | アクセスが常に単一キーの完全一致。JOIN もトランザクションも不要。低レイテンシと水平スケールが最優先 |
| キャッシュヒット率 | URLのアクセスがZipf分布になることはあるが、上位1%で90%という数字は仮定。実アクセスの分布、TTL、項目サイズから測定する |
| クリック分析の負荷 | リダイレクトの同期パスに入れない。Kafka に投げて非同期集計。ユニーク数は HyperLogLog |
| 有効期限切れ | TTL(DynamoDB TTL / Cassandra TTL)で自動削除。バッチでの掃除も併用 |
| 悪意ある URL | 作成時にセーフブラウジング API でチェック、レート制限、報告機能 |
| 同じ URL の重複 | 方式 C では毎回別キーになる。同一化したければ hash(long_url) の逆引きインデックスを持つ(ただしカスタム期限と両立しない点に注意) |
1-7. まとめの言い方
「読み取りが 100 倍支配的なので、リダイレクトパスを最短にすることに集中しました。 キーは事前生成方式で衝突チェックを排除し、 Redis と CDN の 2 層キャッシュでほとんどのリクエストを DB に到達させません。 分析は非同期化してリダイレクトのレイテンシに影響させない構成です。」
Q 02ニュースフィード / Twitter のタイムライン★★★(最頻出。必ずマスターすること)解答を開く
難易度: ★★★(最頻出。必ずマスターすること)
2-1. 要件
機能: ①投稿 ②ホームタイムライン(フォロー中の人の投稿を新しい順) ③フォロー
非機能: DAU 2 億、読み:書き = 100:1、タイムラインの p99 < 200 ms
投稿は数秒遅れて見えてよい(結果整合で可)
2-2. 見積もり(第 3 章の再掲)
書き込み 4,000 QPS(ピーク 1.2 万)、読み取り 40 万 QPS(ピーク 120 万)
ツイート本文 = 400 GB/日、5 年で 730 TB(レプリカ込み 2.2 PB)
→ **読み取り最適化が全て。シャーディング必須。**
2-3. 核心:ファンアウト戦略
方式 A: Fan-out on Read(Pull 型)
タイムライン取得時に、フォロー中 N 人の最新投稿を取得してマージソート
読み: 遅い(N=500 なら 500 回のクエリ or 大きな IN 句)
書き: 速い(自分のテーブルに 1 行入れるだけ)
方式 B: Fan-out on Write(Push 型) ⭐ 基本
投稿時に、全フォロワーのタイムラインキャッシュに ID を配る
読み: 極めて速い(自分のリストを取るだけ = O(1))
書き: 重い(フォロワー 1 億人なら 1 億回の書き込み)
📐 答えはハイブリッド:
通常ユーザー(フォロワー < 10 万)→ Push(書き込み時にファンアウト)
セレブ(フォロワー ≥ 10 万) → Push しない
タイムライン取得時:
① 事前計算済みのタイムラインを Redis から取得(99% はこれ)
② フォロー中のセレブの投稿を別途取得(数人分だけなので軽い)
③ 2 つをマージして時系列にソート → 返す
🎤 この「ハイブリッド」に自力で辿り着くのが、この問題の合格ラインです。
2-4. データモデル
tweets(tweet_id PK[Snowflake], user_id, content, media_urls, created_at)
→ シャードキー: tweet_id(Snowflake なので時刻順、範囲でも引ける)
or user_id(ユーザーの全投稿取得が 1 シャードで済む)
follows(follower_id, followee_id, created_at)
→ 2 方向必要: PK(follower_id, followee_id) と 逆引きテーブル/インデックス
timeline_cache: Redis の List or Sorted Set
key = "timeline:{user_id}"、value = tweet_id のリスト(最新 800 件のみ保持)
📐 タイムラインには tweet_id だけ入れる(本文は入れない)。 理由: ① メモリ節約(8 B × 800 = 6.4 KB/ユーザー) ② 投稿が削除・編集されたときに整合する ③ 本文は別途 multi-get(Redis の MGET で 1 往復)
2-5. アーキテクチャ
【投稿パス】
[Client] → [API GW] → [投稿サービス] → [tweets DB(シャード)]
│
└→ [Kafka: tweet_created]
▼
[ファンアウトワーカー]
① フォロワー一覧を取得
② セレブなら何もしない
③ 各フォロワーの Redis timeline に LPUSH + LTRIM 800
④ 非アクティブユーザー(30日ログインなし)はスキップ ← 重要な最適化
【読み取りパス】
[Client] → [API GW] → [タイムラインサービス]
① Redis から tweet_id 800 件を取得
② フォロー中セレブの最新投稿を取得(別キャッシュ)
③ マージ + ソート + ページング
④ tweet_id → 本文を Redis MGET(ミス分だけ DB)
⑤ ユーザー情報・画像 URL を付与して返す
2-6. 深掘り
| 質問 | 回答 |
|---|---|
| セレブの閾値は? | 固定値ではなく、フォロワー数 × 投稿頻度 のコストで判断。動的に切り替える |
| ファンアウトの遅延は? | フォロワー 1 万人でも並列書き込みで数秒。自分の投稿だけはクライアント側で即座に表示(楽観的 UI)してごまかす |
| キャッシュが落ちたら? | Pull 型にフォールバック(遅いが動く)。復旧時は徐々に再構築 |
| タイムラインの再構築 | 新規フォロー時、キャッシュ喪失時 → 直近 N 件を DB から再計算(バックグラウンド) |
| 順序は厳密? | Snowflake ID で時刻順。ミリ秒の厳密性は不要(結果整合) |
| メディア | 本文と分離。オブジェクトストレージ + CDN。DB には URL のみ |
| ランキング(時系列でなく関連度順) | 候補生成(1000 件)→ 特徴量抽出 → ML でスコアリング → 多様性調整。2 段構成 |
| 非アクティブユーザー | ファンアウト対象から除外(コストの大半を削減できる)。ログイン時に Pull で再構築 |
2-7. まとめ
「読み:書き = 100:1 なので、書き込み時に読み取りの仕事を済ませる(事前計算) のが基本方針です。 ただしフォロワーが極端に多いアカウントでは書き込みコストが爆発するため、 そこだけ読み取り時マージに切り替えるハイブリッドにしました。 ボトルネックはファンアウトワーカーのスループットで、 Kafka のパーティション数とワーカー数でスケールさせます。」
Q 03チャットシステム(WhatsApp / Slack)★★★解答を開く
難易度: ★★★
3-1. 要件
機能: ①1対1チャット ②グループチャット(最大 500 人) ③オンライン状態
④既読・配信済み ⑤オフライン中のメッセージ受信 ⑥メッセージ履歴
非機能: 低レイテンシ(p99 < 200 ms)、サーバーACK済みメッセージを定義した障害モデル内で失わない、チャンネル内の順序を保つ
DAU 5 億、1 人 40 メッセージ/日
3-2. 見積もり
メッセージ数: 5億 × 40 = 200 億/日 = 20万 QPS(ピーク 60 万 QPS)
サイズ: 200 B/件 → 4 TB/日 → 5 年で 7.3 PB(テキストのみ)
同時接続: DAU の 20% が同時接続 = 1 億接続
→ 1 台 10 万接続なら **1,000 台の接続サーバー**が必要
3-3. 通信方式
📐 WebSocket(双方向・低遅延が必要なので確定)
- モバイルはバックグラウンド時に プッシュ通知(APNs / FCM) へフォールバック。
3-4. アーキテクチャ
[Client] ←WebSocket→ [接続サーバー #1..#1000]
│
┌─────────────┼──────────────┐
▼ ▼ ▼
[セッションレジストリ] [メッセージサービス] [Kafka]
Redis: │ │
user_id → server_id ▼ ▼
[メッセージDB] [プッシュ通知]
Cassandra [既読/配信状態]
メッセージ送信の流れ:
① A が接続サーバー S1 へ送信
② メッセージサービスがチャンネル単位のsequence(必要ならSnowflakeは識別子として別管理)を採番し、Cassandra に永続化
③ A に ACK(「サーバーに届いた」チェックマーク 1 つ)
④ レジストリで B の接続先(S7)を検索
⑤ S7 経由で B へ配信 → B から ACK(チェックマーク 2 つ = 配信済み)
⑥ B がメッセージを開いたら既読 ACK(チェックマーク青 = 既読)
⑦ B がオフラインなら → プッシュ通知 + 未配信キューに保持
3-5. データモデル(Cassandra)
CREATE TABLE messages (
channel_id uuid, -- パーティションキー(1対1なら2人のIDから決定的に生成)
message_id bigint, -- Snowflake(時刻順)
sender_id uuid,
content text,
created_at timestamp,
PRIMARY KEY ((channel_id), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
-- → 「あるチャンネルの最新 N 件」が 1 パーティションへの 1 シークで取れる
CREATE TABLE user_channels (user_id, last_read_message_id, channel_id, PRIMARY KEY(user_id, channel_id));
⚠️ パーティションが大きくなりすぎる問題: 何年も続くチャンネルは巨大化する
→ 時間バケットを複合キーに入れる: PRIMARY KEY ((channel_id, year_month), message_id)
3-6. 深掘り
| 質問 | 回答 |
|---|---|
| 順序保証 | Snowflakeの時刻順だけでは、複数ノードから同一チャンネルへ送った順序を保証できない。チャンネル単位のsequencer/partition、サーバー受信順、または因果順を定義し、同じ順序で永続化する |
| 重複防止 | クライアント生成の client_message_id(UUID)で冪等化。再送しても 1 件 |
| オフライン配信 | 「最後に受信したチャンネルsequence」をクライアントが保持し、再接続時に差分取得。ACKの意味、再送、重複排除、未読状態を定義する |
| グループ配信 | 500 人 × メッセージ = ファンアウト。メッセージ本体は 1 つだけ保存し、配信状態のみ per-user。大規模グループは Push しきれないので購読型に |
| オンライン状態 | Redis に presence:{user_id} を TTL 30 秒で書き、ハートビートで更新。全フレンドに通知するとファンアウトが爆発するので、購読中の相手にだけ配信 or ポーリング |
| エンドツーエンド暗号化 | Signal プロトコル(X3DH + Double Ratchet)。サーバーは暗号文しか持たない → 検索・サーバー側の既読管理・多端末同期が難しくなるトレードオフ |
| 接続サーバーのデプロイ | 全員が同時に再接続するのを防ぐため、段階的にドレイン + 再接続にジッタ |
| メッセージの削除 | トゥームストーンを配信。既に配信済みの端末からも消す(保証はできない) |
3-7. まとめ
「接続の保持(ステートフル)とメッセージ処理(ステートレス)を分離するのが設計の中心です。 接続サーバーは 1,000 台規模になるため、ユーザー → サーバーのマッピングを Redis に持ち、 配信は Pub/Sub で中継します。永続化は Cassandra で、 channel_id をパーティションキー、Snowflake をクラスタリングキーにすることで 『最新 N 件の取得』を単一シークで実現します。」
Q 04動画配信(YouTube / Netflix)★★★★解答を開く
難易度: ★★★★
4-1. 要件
機能: ①動画アップロード ②視聴(ストリーミング) ③検索 ④推薦(今回は軽く)
非機能: 再生開始 < 2 秒、バッファリングを最小化、DAU 10 億、
アップロード 500 時間/分(YouTube 相当)
4-2. 見積もり
アップロード: 500 時間/分 = 8.3 時間/秒
1 時間の動画 = 元ファイル 5 GB → 保存量 = 8.3 × 5 GB = 41 GB/秒 = 3.6 PB/日
トランスコード後の全解像度合計で元の 1.5 倍 → 5.4 PB/日
視聴: 10 億人 × 30 分/日 × 平均 3 Mbps
= 10^9 × 1800 秒 × 3 Mbps / 86400 秒 ≒ 62 Tbps(!)
→ **オリジンでは絶対に不可能。CDN が設計の中心**
📐 この計算で「CDN が全て」という結論に自力で到達することが評価されます。
4-3. アップロードとトランスコードのパイプライン
[Client]
│ ① 署名付き URL を取得
▼
[オブジェクトストレージ(生ファイル)] ← マルチパートで直接アップロード
│ ② イベント通知
▼
[Kafka: video_uploaded]
▼
[トランスコードパイプライン(DAG ワークフロー)]
├ 検査(コーデック、破損、著作権 = コンテンツ ID)
├ 分割(動画を 10 秒チャンクに分けて並列処理)★ ここが速度の鍵
├ 並列トランスコード(240p/360p/480p/720p/1080p/4K × 複数コーデック)
├ サムネイル生成、字幕生成(音声認識)
├ セグメント化(HLS/DASH: 2〜6 秒セグメント + マニフェスト)
└ 結合・検証
▼
[オブジェクトストレージ(配信用)] → [CDN へプリウォーム(人気が予想されるものだけ)]
▼
[メタデータ DB を "published" に更新] → 検索インデックス更新
📐 並列化が鍵: 1 時間の動画を直列にトランスコードすると数時間かかる。 10 秒チャンクに分割 → 数百ノードで並列 → 結合 で数分に短縮します。 ⚠️ チャンク境界を キーフレーム (GOP) 境界に合わせないと繋ぎ目が壊れます。
4-4. 配信(ABR ストリーミング)
[Player] → マニフェスト(.m3u8 / .mpd)を取得
→ 帯域を推定 → 適切なビットレートのセグメントを順次取得
→ 帯域が落ちたら次のセグメントから低画質へ切替(途切れない)
[Player] → [CDN エッジ](99% がここでヒット)
│ ミス
▼
[リージョナルキャッシュ(シールド)]
│ ミス
▼
[オリジン(オブジェクトストレージ)]
📐 CDN のコンテンツ配置戦略:
- 人気動画(上位 1%): 全エッジにプッシュ配置(プリポジション)
- 中位: リージョナルキャッシュに保持、アクセスでエッジへ昇格
- ロングテール: オリジンからプル。キャッシュミスを許容 🔬 Netflix の Open Connect は、ISP の建物内にサーバーを置き、 深夜の空き帯域で人気コンテンツを事前配信します。
4-5. データモデル
videos(video_id PK, user_id, title, description, duration, status,
created_at, thumbnail_url, manifest_url) → RDB or Spanner
video_renditions(video_id, resolution, bitrate, codec, storage_path)
view_counts(video_id, count) → Redis + 定期永続化
watch_history(user_id, video_id, position, watched_at) → Cassandra
comments(video_id, comment_id, ...) → Cassandra
4-6. 深掘り
| 質問 | 回答 |
|---|---|
| 再生開始を速くするには | 最初のセグメントは低ビットレートから開始、マニフェストの事前取得、TCP/QUIC 接続の事前確立、CDN の近接性 |
| 視聴回数のカウント | 全視聴を DB に書くのは不可能 → Kafka → ストリーム集計 → 定期的に DB へ。表示は近似値、正確な数値は日次バッチ |
| 不正な再生回数 | 重複排除(ユーザー+動画+時間窓)、ボット検知、最低視聴時間の閾値 |
| ライブ配信の違い | 低遅延が必須(LL-HLS / WebRTC)、セグメントを短く(1〜2 秒)、事前トランスコードできない(リアルタイム処理) |
| ストレージコスト | ロングテール動画は低解像度のみ保持し、高解像度はオンデマンド生成。古い/不人気動画は低コスト階層へ |
| 著作権 | アップロード時に指紋(fingerprint)を生成し、既知コンテンツと照合(Content ID) |
| 地域制限 | CDN のエッジで地理判定、署名付き URL に地域制約を含める |
4-7. まとめ
「視聴帯域が数十 Tbps に達するため、設計の中心は CDN による配信です。 オリジンへのリクエストは 1% 未満に抑える必要があります。 アップロード側は、動画をチャンクに分割して並列トランスコードすることで 処理時間を線形に短縮します。 全体を非同期パイプラインにし、各段階を独立にスケール・リトライできる構成にしました。」
Q 05クラウドストレージ(Google Drive / Dropbox)★★★★解答を開く
難易度: ★★★★
5-1. 要件
機能: ①ファイルのアップロード/ダウンロード ②複数デバイス間の同期
③共有と権限 ④バージョン履歴 ⑤オフライン編集後の同期
非機能: 帯域の効率(差分のみ転送)、競合の解決、大きなファイル(数 GB)対応
5-2. 核心:チャンク化と重複排除
ファイル → 可変長チャンク(平均 4 MB、Rabin fingerprint による境界決定)に分割
→ 各チャンクの SHA-256 を計算
→ サーバーに「このハッシュ持ってる?」と問い合わせ
→ 持っていないチャンクだけアップロード
📐 可変長チャンクが重要な理由:
固定長(4MB ごと)だと、ファイルの先頭に 1 バイト挿入しただけで
全チャンクの境界がずれ、全チャンクが「新しいチャンク」になってしまう
可変長(内容によって境界を決める)なら、変更箇所の周辺チャンクだけが変わる
効果: 同じファイルを 100 人が持っていても保存は 1 つ。 1 GB のファイルの一部を編集しても数 MB の転送で済む。
5-3. データモデル
files(file_id PK, owner_id, parent_folder_id, name, size, current_version_id, updated_at)
versions(version_id PK, file_id, chunk_list[], created_at, created_by, size)
chunks(chunk_hash PK, storage_path, ref_count, size) ← 重複排除の要
permissions(file_id, principal_id, role) ← owner/editor/viewer
device_sync_state(device_id, user_id, last_sync_cursor)
📐 バージョン = チャンクハッシュのリスト。これにより 「バージョン間の差分」も「重複排除」も同じ仕組みで実現できます。
5-4. アーキテクチャ
[Client(デスクトップアプリ)]
├ ファイル監視(inotify / FSEvents)
├ チャンカー(分割 + ハッシュ)
├ ローカル DB(SQLite: ファイル状態、同期状態)
└ 同期エンジン
│
├─① メタデータ API →[メタデータサービス]→[メタデータ DB]
│ │
├─② チャンクの有無を問い合わせ →[チャンクサービス]
│
├─③ 未保有チャンクを直接アップロード →[オブジェクトストレージ]
│
└─④ 変更通知の購読 ←[通知サービス(WebSocket/ロングポーリング)]
▲
[変更ログ(Kafka)]
同期の流れ:
【アップロード側】
ローカル変更を検知 → チャンク化 → 未保有チャンクのみアップロード
→ メタデータをコミット(新バージョン作成)→ 変更ログに記録
【ダウンロード側】
変更通知を受信(or 定期ポーリング)
→ 自分の last_sync_cursor 以降の変更差分を取得
→ 未保有チャンクのみダウンロード → ローカルに再構成
📐 変更をカーソル(単調増加のシーケンス番号)で管理するのが要点。 「前回どこまで同期したか」だけを覚えていれば、確実に差分同期できます。
5-5. 深掘り
| 質問 | 回答 |
|---|---|
| 競合(2 台で同時編集) | ① 楽観ロック(バージョン番号)で検出 → ② 自動マージは一般に不可能なので 両方を保存(file (conflicted copy from device X).docx)。テキストなら 3-way マージや CRDT も選択肢 |
| 大きなファイル | マルチパート + レジューム。チャンク単位なので中断に強い |
| 参照カウント | 削除時に ref_count を減らし、0 になったチャンクを遅延 GC。削除は即時にせずゴミ箱期間を設ける |
| 共有フォルダの権限 | フォルダ階層の継承。判定コストが高いので、実効権限をキャッシュ or 非正規化。🔬 Google の Zanzibar 方式(ReBAC)が本格解 |
| オフライン編集 | ローカル DB に変更を蓄積し、再接続時にまとめて同期。競合はその時に解決 |
| 帯域制御 | クライアント側でスロットリング、深夜の同期優先度調整 |
| メタデータのスケール | ファイル数は膨大(10 億ユーザー × 数千ファイル)。user_id でシャーディング。共有はクロスシャードになるので、共有関係は別テーブル |
| 暗号化 | クライアント側暗号化(E2EE)にすると重複排除が効かなくなる(同じ内容でも暗号文が違う)。収束暗号 (convergent encryption) で両立可能だが、辞書攻撃のリスクがある |
5-6. まとめ
「設計の中心は コンテンツアドレス指定のチャンク化です。 可変長チャンクとハッシュによる重複排除で、ストレージと帯域を大幅に削減できます。 同期はカーソルベースの差分取得にすることで、 オフライン後の再接続でも確実に整合します。 競合は自動解決を諦めて両方を保存する方針が、ユーザーのデータを失わない最も安全な選択です。」
Q 06配車サービス(Uber / Lyft)★★★★解答を開く
難易度: ★★★★
6-1. 要件
機能: ①ドライバーの位置更新 ②近くのドライバー検索 ③マッチング ④乗車中の追跡 ⑤料金計算
非機能: マッチングは 5 秒以内、位置更新は 100 万台 × 4 秒ごと、
一度に 2 人の乗客を同じドライバーに割り当ててはいけない(強い一貫性が必要な箇所)
6-2. 見積もり
位置更新: 100 万ドライバー / 4 秒 = 25 万 QPS(書き込み!)
→ 永続 DB には書けない。**インメモリ + 非同期永続化**
乗車リクエスト: 1 日 2,000 万件 = 200 QPS(ピーク 1,000 QPS)
→ マッチングの QPS は低い。問題は位置更新の書き込み量
📐 この問題の本質: 「読み取りが重い」他の問題と違い、 書き込み(位置更新)が支配的である点。ここに気づけるかが評価点です。
6-3. アーキテクチャ
[ドライバーアプリ] ─4秒ごと─► [位置更新サービス(多数、ステートレス)]
│
├─► [Redis GEO / インメモリグリッド](現在位置)
│ key = geo:{city_id} → 全ドライバーの位置
└─► [Kafka] → [位置履歴(Cassandra/S3)] 非同期
[乗客アプリ] ─乗車要求─► [マッチングサービス]
① 乗客位置から H3/Geohash セルを算出
② 自セル + 隣接セルのドライバーを取得(50〜200 人)
③ 利用可能・車種・評価でフィルタ
④ ETA を計算(道路距離、経路サービス)
⑤ 短い有効期限で複数候補へオファーし、条件付き更新で最初の承諾だけを確定
⑥ 承諾 → **配車を確定(ここだけ強い一貫性)**
6-4. 一貫性が必要な箇所を切り分ける(この問題の核心)
【結果整合で十分】ドライバーの位置(数秒古くても実害なし)
【強い一貫性が必須】配車の確定(1 ドライバー = 1 乗車)
実装:
ドライバーの状態を単一の権威ある場所(RDB の 1 行 or 分散ロック)で管理し、
条件付き更新で確定する:
UPDATE drivers SET status='assigned', trip_id=?
WHERE driver_id=? AND status='available'; -- 更新行数 0 なら他に取られた
🎤 「システム全体で一貫性レベルを分ける」 と言えるのが L5 レベルの答え方です。
6-5. データモデル
drivers(driver_id PK, status, current_trip_id, vehicle_type, rating, updated_at) -- RDB
driver_locations: Redis GEO(永続化しない)
trips(trip_id PK, rider_id, driver_id, status, start_loc, end_loc,
requested_at, accepted_at, completed_at, fare) -- RDB、city でシャード
trip_locations(trip_id, timestamp, lat, lng) -- Cassandra(走行履歴)
6-6. 深掘り
| 質問 | 回答 |
|---|---|
| なぜ位置を永続化しない? | 25 万 QPS の書き込みは高コストで、かつ 4 秒で無価値になる。履歴は非同期で安いストレージへ |
| 地理シャーディング | 都市単位でシャード。都市を跨ぐ移動は稀なので、独立性が高い。ホットな都市はさらに細分化 |
| サージプライシング | セル単位で需要(リクエスト数)と供給(空きドライバー数)を集計し、比率で係数を決定。ストリーム処理で数十秒周期に更新 |
| マッチングアルゴリズム | 単純な最近傍だとローカル最適。バッチマッチング(数秒分をまとめて二部グラフの最適割当=ハンガリアン法)にすると全体の待ち時間が改善 |
| オファーの競合 | 並列オファーは有効期限、予約トークン、条件付き更新でwinnerを一人に確定する。逐次オファーなら、5秒のマッチング目標と各オファーの待ち時間が矛盾しないようにする |
| ネットワーク断 | ドライバーアプリが位置をローカルにバッファし、復帰時にまとめて送信。乗車中の状態はアプリ側にも保持 |
| ETA の計算 | 直線距離では不正確。道路グラフ上の経路探索(コントラクション階層等)+ 交通状況の予測モデル。重いのでキャッシュとセル単位の近似を併用 |
| 決済 | 乗車完了 → 非同期で決済(Saga)。失敗しても乗車は完了扱いにし、後で回収 |
6-7. まとめ
「この問題では書き込み(位置更新 25 万 QPS)が支配的なので、 位置情報はインメモリのジオインデックスに保持し、履歴は非同期で永続化します。 一方、配車の確定だけは二重割当が許されないため、 ドライバーの状態を単一の権威に置いて条件付き更新で確定します。 一貫性レベルを機能ごとに分けることが、この設計の要点です。」
Q 07Web クローラ★★★★解答を開く
難易度: ★★★★
7-1. 要件
機能: ①シードURLから開始 ②HTMLを取得・解析 ③リンクを抽出して巡回 ④コンテンツを保存
非機能: 月 10 億ページ、robots.txt 遵守、礼儀正しさ(同一ドメインへの負荷制限)、
重複排除、無限ループの回避、拡張性
7-2. 見積もり
10 億ページ/月 = 10^9 / (30 × 10^5) ≒ 400 ページ/秒
1 ページ 500 KB(HTML + リソース)→ 500 TB/月(HTML のみなら 100 KB で 100 TB/月)
帯域: 400 × 500 KB = 200 MB/s = 1.6 Gbps
7-3. アーキテクチャ
[シード URL]
▼
┌──────────────────────────────────────────────┐
│ URL フロンティア(優先度付きキュー群) │
│ ├ 優先度キュー(PageRank、更新頻度で決定) │
│ └ ドメイン別キュー(同一ホストへのレート制限) │ ★ 礼儀の要
└──────────────────────────────────────────────┘
▼
[DNS 解決(キャッシュ必須)]
▼
[ダウンローダ(数千の並行接続、非同期 I/O)]
├ robots.txt チェック(キャッシュ)
└ タイムアウト、サイズ上限、リダイレクト上限
▼
[コンテンツ重複判定] ← ドキュメントのハッシュ / SimHash(近似重複)
▼
[パーサ] HTML 解析 → 本文抽出、リンク抽出、正規化
▼
[URL フィルタ] robots、拡張子、ブラックリスト、URL 正規化
▼
[URL 重複判定] ← Bloom フィルタ(訪問済み)
▼
[URL フロンティアへ戻す] + [コンテンツを保存(S3/HDFS)→ インデックス構築へ]
7-4. 核心的な設計要素
| 要素 | 設計 |
|---|---|
| 礼儀正しさ (politeness) | 同一ホストへの同時接続数、リクエスト間隔、応答・エラーを制御する。robots.txtの方針と、サイトが明示する指示(Crawl-delayは実装・サイト依存)を確認 |
| URL の重複排除 | 10 億 URL × 100 B = 100 GB → メモリに載らない。Bloom フィルタ(1% 誤検知で 1.2 GB)+ 正確性が必要なら分散 KV |
| URL 正規化 | scheme/hostの大文字小文字、デフォルトポート、フラグメントなど仕様上安全な範囲だけを正規化する。パスやクエリの大文字小文字変更、クエリの並べ替え、セッションID除去は意味を変え得るためサイトごとに確認 |
| コンテンツ重複 | 完全一致はハッシュ、近似重複は SimHash(ハミング距離 3 以内を同一とみなす) |
| クローラトラップ | 無限に生成されるカレンダーページ等 → URL の深さ制限、ドメインあたりのページ数上限、パターン検知 |
| 優先度 | PageRank、更新頻度(ニュースは高頻度)、被リンク数 |
| 再クロール | ページの変化率を学習し、適応的に間隔を決める(ポアソン過程モデル) |
| 分散 | URLをホスト単位でシャーディングし、同一ホストのpoliteness状態を一箇所で管理する。DNS rebinding、SSRF、内部IP、圧縮爆弾、巨大リダイレクトも防ぐ |
7-5. 深掘り
| 質問 | 回答 |
|---|---|
| JavaScript レンダリング | ヘッドレスブラウザが必要だがコストが 10〜100 倍。2 段構成(まず HTML、必要なものだけレンダリング) |
| ノード障害 | フロンティアは永続化(Kafka や DB)し、処理中 URL はリース方式(タイムアウトで再割当) |
| クロール速度の制御 | 相手サーバーの応答時間・エラー率に応じて適応的に減速(相手を壊さない) |
| 同じ内容の別 URL | canonical タグの尊重、リダイレクトの追跡、SimHash |
| 保存形式 | WARC 形式 or 圧縮した生 HTML + メタデータ。列指向で解析用に別途変換 |
7-6. まとめ
「クローラの本質は 『URL フロンティアの管理』 です。 優先度と礼儀正しさという 2 つの制約を同時に満たすため、 優先度キューとホスト別キューの二層構造にします。 URL の重複排除は 10 億件規模なので Bloom フィルタを使い、 ホストのハッシュでシャーディングすることで、 各ノードが独立にレート制限を管理できる構成にします。」
Q 08検索オートコンプリート(サジェスト)★★★解答を開く
難易度: ★★★
8-1. 要件
機能: 入力中のプレフィックスに対し、人気のクエリ上位 5〜10 件を返す
非機能: p99 < 100 ms(**キー入力ごとに呼ばれるので極めて厳しい**)、
1 日 100 億クエリ、リアルタイム性は数時間遅れでも可
8-2. 見積もり
検索クエリ 100 億/日。1 クエリで平均 20 文字入力 → デバウンスして 4 回リクエスト
→ 400 億リクエスト/日 = 40 万 QPS(ピーク 120 万 QPS)
→ **完全にキャッシュ/メモリで応答する必要がある**
8-3. データ構造:Trie(トライ木)
(root)
/ \
s t
/|\ \
y c ... o
/ \ \
s h k
(system:5000) (tokyo:9000)
各ノードに「そのプレフィックスで始まる上位 K 件」を事前計算して保持する
→ 検索は O(プレフィックス長) で、その後のソートが不要
📐 最重要の最適化: 各ノードに Top-K をキャッシュする。 これがないと、そのノード以下の全部分木を走査してソートする必要があり、間に合いません。
8-4. アーキテクチャ
【オフライン(数時間ごと)】
[検索ログ] → [Kafka] → [集計ジョブ(Spark/Flink)]
・クエリの出現回数を集計(時間減衰つき)
・NG ワードのフィルタ
▼
[Trie の構築]
▼
[シリアライズして配布]
▼
【オンライン】
[Client] → [CDN/エッジキャッシュ] → [サジェストサービス(Trie をメモリに保持)]
↑ 人気プレフィックスはここで返る(大半)
📐 Trie は各サービスノードのメモリに丸ごと載せる(数 GB)。 ネットワークを介さないので p99 が守れます。更新はプロセスの再起動 or アトミックな差し替え(ダブルバッファ)。
8-5. 深掘り
| 質問 | 回答 |
|---|---|
| Trie が大きすぎる場合 | プレフィックス(最初の 1〜2 文字)でシャーディング。または圧縮 Trie(Radix Tree)、DAWG、FST(有限状態トランスデューサ、Lucene が採用) |
| リアルタイム更新 | Trie は更新が重い。「安定した大きな Trie」+「直近のホットワードの小さな差分」を両方引いてマージする 2 層構成 |
| パーソナライズ | グローバルな Top-K + ユーザーの検索履歴をマージ。履歴側は端末ローカルでもよい |
| タイポ許容 | 編集距離 1〜2 の候補(レーベンシュタインオートマトン)、キーボード配列を考慮した誤字辞書 |
| 多言語・日本語 | ローマ字入力の途中段階("tou" → "東京")に対応するため、読み仮名も Trie に入れる |
| 不適切語のフィルタ | オフライン集計時にブラックリスト適用(オンラインでやると遅い) |
| レイテンシ削減 | クライアント側デバウンス(150 ms)、先読みキャッシュ、CDN、HTTP/2 の多重化 |
8-6. まとめ
「キー入力ごとに呼ばれるためレイテンシ要件が極端に厳しく、 オンラインでの計算を一切しない設計にします。 オフラインで Trie を構築し、各ノードに Top-K を事前計算して埋め込み、 サービスノードのメモリに丸ごと載せます。 リアルタイム性は数時間遅れで許容し、 トレンドだけ小さな差分 Trie で補う 2 層構成にします。」
Q 09通知システム(Push / メール / SMS)★★★解答を開く
難易度: ★★★
9-1. 要件
機能: ①複数チャネル(iOS Push / Android Push / SMS / メール / アプリ内)
②テンプレート ③ユーザーの設定(オプトアウト) ④配信状況の追跡
⑤スケジュール配信・一斉配信
非機能: 1 日 10 億通知、重要な通知は失わない、重複配信を避ける、
サードパーティ(APNs/FCM/SendGrid)の障害に耐える
9-2. アーキテクチャ
[各サービス] ──通知イベント──► [通知 API]
│ ① 検証・重複排除(冪等キー)
▼
[Kafka: notifications]
▼
[通知ワーカー(プランナー)]
② ユーザー設定を確認(オプトアウト、時間帯、頻度上限)
③ テンプレートをレンダリング(多言語対応)
④ 送信先チャネルとデバイストークンを決定
▼
┌─────────────┼─────────────┐
▼ ▼ ▼
[iOS キュー] [Android キュー] [メールキュー] ← チャネルごとに分離
▼ ▼ ▼
[APNs 送信] [FCM 送信] [SES/SendGrid]
└─────────────┼─────────────┘
▼
[配信結果イベント] → 分析・リトライ・無効トークン削除
📐 チャネルごとにキューを分ける理由(バルクヘッド): メールプロバイダが遅くなっても、プッシュ通知は影響を受けない。
9-3. データモデル
notification_templates(template_id, channel, locale, subject, body)
user_preferences(user_id, channel, category, enabled, quiet_hours_start/end, timezone)
device_tokens(user_id, device_id, platform, token, updated_at, is_valid)
notification_log(notification_id, user_id, channel, status, sent_at, delivered_at, error)
→ Cassandra(大量・時系列・検索は user_id 単位)
9-4. 深掘り
| 質問 | 回答 |
|---|---|
| 重複配信の防止 | イベントに冪等キーを持たせ、(user_id, event_key) で処理済みを記録(Redis の SETNX、TTL 24h) |
| 一斉配信(1 億人) | 一度に投げると自分もプロバイダも死ぬ。セグメントを分割し、レート制限しながら段階配信。バッチ ID で進捗管理 |
| サードパーティ障害 | サーキットブレーカ + DLQ + リトライ(指数バックオフ)。代替チャネルへのフォールバック |
| 優先度 | 「2 段階認証コード」と「マーケティング通知」を同じキューに入れない。優先度別キューを用意 |
| 通知疲れ対策 | ユーザーあたりの頻度上限(1 日 N 件)、まとめ通知(digest)、重要度によるフィルタ |
| 時間帯配慮 | ユーザーのタイムゾーンで深夜を避ける → スケジューラで遅延配信 |
| 無効トークン | APNs/FCM のフィードバックで無効を検知し、DB から削除(放置すると送信コストとエラー率が悪化) |
| 配信確認 | 送信済み ≠ 到達。プッシュは到達確認が弱いので、アプリ側からの開封イベントで補完 |
9-5. まとめ
「通知システムの要点は チャネルごとの分離とレート制御です。 チャネルごとにキューとワーカーを分けることで、あるプロバイダの障害が 他チャネルに波及しないようにします。 一斉配信はセグメント分割 + レート制限で段階的に行い、 ユーザー設定と頻度上限をプランナー段階で必ず適用します。」
Q 10分散レート制限サービス★★☆解答を開く
難易度: ★★☆(第 24 章の知識をそのまま使う)
10-1. 要件
機能: ユーザー/API キー/IP 単位で「N リクエスト / 時間窓」を強制する
複数のルール(グローバル、エンドポイント別、プラン別)
非機能: 追加レイテンシ < 5 ms、レート制限サービス自体が落ちても本体は動く、
10 万 QPS のチェック
10-2. 設計
[Client] → [API Gateway]
│ ① ルールを取得(ローカルキャッシュ、設定は Config サービスから配信)
│ ② カウントを確認・更新
▼
【二層方式】
ローカルカウンタ(プロセス内、トークンバケット)
│ 100 ms ごとに同期
▼
[Redis クラスタ](グローバルカウンタ、Lua で原子的に)
📐 二層方式が実務の答え:
- ローカルだけ → 台数分だけ超過する
- 中央だけ → レイテンシと Redis への負荷が高い
- 二層 → 各ノードにクォータを配分し、超えそうになったら中央に問い合わせる (わずかな超過を許容する代わりに、レイテンシとコストを劇的に下げる)
10-3. 深掘り
| 質問 | 回答 |
|---|---|
| Redis が落ちたら | フェイルオープン(通す)が基本。レート制限のために本体を止めるのは本末転倒。ただし DDoS 防御目的ならフェイルクローズも検討 |
| ホットキー | 巨大テナントのカウンタが 1 ノードに集中 → キーをサフィックスで分割(key#0〜key#9)し、上限も 1/10 ずつ配分 |
| 時計のずれ | Redis サーバーの時刻を基準にする(TIME コマンド)。クライアントの時計は信用しない |
| ルールの動的変更 | 設定を Config サービスから配信し、ローカルキャッシュを数秒で更新。段階展開する |
| 多層防御 | CDN/WAF(IP 単位)→ ゲートウェイ(キー単位)→ サービス内(コスト単位、例: LLM のトークン数) |
Q 11決済システム★★★★★(正確性が最優先の問題)解答を開く
難易度: ★★★★★(正確性が最優先の問題)
11-1. 要件
機能: ①支払いの受付 ②決済代行への連携 ③返金 ④台帳の記録 ⑤精算
非機能: **絶対に二重請求しない**、絶対に金額を失わない、監査可能、
整合性 > 可用性、PCI DSS 準拠
📐 他の問題と根本的に違う点: ここでは 可用性より正確性が優先されます。 「とりあえず結果整合で」は不正解です。
11-2. 核心的な設計原則
① 複式簿記(Double-Entry Bookkeeping)
すべての取引を「借方」と「貸方」の対で記録し、合計が必ずゼロになるようにする
取引 tx_001(ユーザーが 1000 円支払う):
ledger_entries:
(tx_001, account=user_wallet, amount=-1000)
(tx_001, account=merchant_payable, amount=+1000)
→ SUM(amount) = 0 が常に成立(これが検算になる)
📐 お金は「更新」しない。「追記」する。 残高は「エントリの合計」として導出します。 これにより監査可能性と、不整合の検出可能性が得られます。
② 冪等性キー(第 9・18 章)
POST /payments Idempotency-Key: <client-uuid>
→ 一意制約付きテーブルに記録し、既存なら保存済みレスポンスを返す
→ **ネットワークタイムアウト時のリトライで二重請求が起きない**
③ 状態機械
[created] → [authorized] → [captured] → [settled]
│ │ └→ [refunded]
└→ [failed] └→ [voided]
・遷移は必ず条件付き更新: UPDATE ... WHERE status = '想定の現在状態'
・不可逆な遷移は元に戻さない(返金は「取り消し」ではなく新しい取引)
④ Saga(第 18 章): 外部決済ゲートウェイとの連携は 2PC が使えないので、 状態機械 + 補償トランザクション(返金)で整合を取ります。
11-3. アーキテクチャ
[Client] → [決済 API]
① 冪等性キーの確認
② 取引レコードを created で作成(自 DB のトランザクション)
▼
[決済オーケストレーター]
③ 決済代行(Stripe/Adyen)へ authorize
④ 結果を記録(authorized / failed)
⑤ capture(即時 or 後日)
▼
[台帳サービス(append-only)]
⑥ 複式簿記のエントリを記録
▼
[イベント発行(Outbox)] → 注文サービス、通知、分析
【非同期】
[Webhook 受信] ← 決済代行からの状態変更通知(**必ず署名検証 + 冪等処理**)
[照合バッチ] ← 毎日、自社台帳と決済代行の明細を突合(reconciliation)
📐 照合(reconciliation)は必須です。 どれだけ設計しても、外部システムとの間には必ず不一致が発生します。 「毎日突合し、差分を検出してアラートする」 仕組みがないシステムは本番で使えません。 🎤 面接でこれに言及できると、金融ドメインの理解として非常に高く評価されます。
11-4. データモデル
payments(payment_id PK, idempotency_key UNIQUE, user_id, amount, currency,
status, provider, provider_ref, created_at, updated_at, version)
ledger_entries(entry_id PK, transaction_id, account_id, amount, currency,
created_at) -- **UPDATE も DELETE もしない(追記専用)**
制約: 同一 transaction_id の amount 合計 = 0
webhook_events(event_id PK[provider の ID], payload, processed_at) -- 重複排除
11-5. 深掘り
| 質問 | 回答 |
|---|---|
| タイムアウトで結果不明のとき | 「不明」を状態として持つ。推測で成功にも失敗にもしない。決済代行へ照会 API で確認(同じ冪等キーで再送すれば結果が返る) |
| 通貨と丸め | 浮動小数点を絶対に使わない。最小単位の整数(円なら 1、ドルならセント)または decimal。丸めルールを明示 |
| 同時実行 | 口座残高は行ロック or 条件付き更新。ホットな口座(大手加盟店)はエントリの追記のみにして残高計算を非同期化 |
| PCI DSS | カード番号を自社で保持しない。トークン化(決済代行が発行するトークンを保存)。保持するならスコープを最小の隔離環境に |
| 不正検知 | 同期パス(低レイテンシのルールエンジン)+ 非同期(ML モデル)の 2 段。速度と精度の分離 |
| 可用性より一貫性 | 決済代行が落ちたら受け付けない(失敗を返す)方が、二重請求より遥かに良い。ただしキューに入れて後で処理する設計も選択肢(その場合は「保留中」をユーザーに示す) |
| 返金 | 元取引を書き換えず、新しい逆方向の取引として記録する |
11-6. まとめ
「決済システムでは可用性より正確性を優先します。 設計の柱は 3 つです。① 冪等性キーによる二重請求の防止、 ② 追記専用の複式簿記台帳による監査可能性と検算、 ③ 明示的な状態機械による曖昧な状態の排除です。 さらに、外部システムとの不一致は必ず発生するため、 日次の照合バッチを運用の一部として最初から組み込みます。」
Q 12分散ジョブスケジューラ(cron as a service)★★★★解答を開く
難易度: ★★★★
12-1. 要件
機能: ①ジョブの登録(cron 式 or 1 回限り) ②時刻に実行 ③リトライ ④実行履歴
非機能: 100 万ジョブ、秒精度、**重複実行を避ける**、
ワーカー障害時も実行される(at-least-once)
12-2. 設計の核心
【素朴な設計の問題】
全ジョブを 1 秒ごとにスキャンする → 100 万件のスキャンが毎秒。無理
【解1: 時間バケット(タイムホイール)】
ジョブを「実行予定時刻の分」でバケット化して保存
jobs_by_minute: (execute_minute) → [job_id, ...]
→ 毎分、次の 1 分のバケットだけを読む(数千件)
→ メモリ上のタイムホイール(階層型)で秒精度の発火
【解2: 優先度キュー / ソート済みインデックス】
Redis Sorted Set(score = 実行時刻の UNIX 秒)
ZRANGEBYSCORE jobs 0 <now> LIMIT 0 100 → 実行すべきものだけ取得
→ シンプルで実用的。100 万件でも O(log N + M)
12-3. アーキテクチャ
[API] → [ジョブ DB(永続、真実の情報源)]
│ 次の N 分のジョブを先読み
▼
[スケジューラ(リーダー選出: Raft/etcd で 1 台がアクティブ)]
│ 実行時刻になったら
▼
[実行キュー(Kafka / SQS)]
▼
[ワーカー(多数、ステートレス)]
① メッセージを取得(可視性タイムアウト付き)
② **実行状態をclaim**: (job_id, scheduled_time) を一意にし、queued/running/succeeded/failed と lease_until、attempt、owner_epoch を保存
→ lease切れのrunningは回収し、同じ実行を無期限に捨てない
③ 実行 → 結果を記録。副作用はジョブ側で冪等にし、fencing tokenを検証する
④ 失敗ならリトライ(指数バックオフ)→ 上限超えで DLQ
▼
[次回実行時刻を計算してスケジュールに戻す(cron の場合)]
12-4. 深掘り
| 質問 | 回答 |
|---|---|
| 重複実行を完全に防げるか | 完全には防げない(ワーカーが実行後、記録前に落ちるケース)。→ ジョブ自体を冪等にし、実行ID、lease、fencing token、再実行・照合の方針を持つ |
| スケジューラの単一障害点 | リーダー選出で 1 台をアクティブに。落ちたら別が引き継ぐ。リースとエポック番号で二重稼働を防ぐ |
| 時刻精度 | ジョブ量が多いと発火が遅れる。先読み時間(1 分先までをメモリに載せる)で吸収。厳密な秒精度が必要なら専用の高優先度パス |
| サンダリングハード | 「毎時 0 分」に大量のジョブが集中 → 要件が許す場合だけ実行ウィンドウ内で分散。厳密な時刻が必要なジョブに勝手なジッタを入れない |
| 長時間ジョブ | 可視性タイムアウトを超えると二重実行される → ハートビートで延長、または実行中フラグをリースで管理 |
| 依存関係のあるジョブ | DAG スケジューラ(Airflow 型)へ拡張。上流の完了イベントをトリガーにする |
| タイムゾーンと夏時間 | cron 式にタイムゾーンを持たせる。夏時間で「存在しない時刻」「2 回ある時刻」の扱いを明示的に決める(実務で必ず問題になる) |
Q 13分散キャッシュ(Memcached / Redis クラスタを作る)★★★解答を開く
難易度: ★★★
13-1. 要件
機能: get / set / delete / TTL、クラスタ構成
非機能: p99 < 1 ms、10 TB のデータ、100 万 QPS、ノード追加削除に耐える
13-2. 設計
【データ配置】コンシステントハッシュ + 仮想ノード(第 16 章)
ノード追加時の移動は K/N のみ。クライアント側でルーティング(プロキシレス)
or 専用プロキシ(twemproxy, Envoy)を経由
【単一ノードの構造】
ハッシュテーブル(キー → 値)
+ LRU 連結リスト(追い出し順)
+ スラブアロケータ(メモリ断片化の防止:サイズクラス別にチャンクを管理)
+ イベントループ(epoll/kqueue)で数万接続を少数スレッドで処理
【高可用性】
各シャードに 1 プライマリ + N レプリカ(非同期複製)
クライアントは読みをレプリカへ分散可(結果整合を許容する場合)
13-3. 深掘り
| 質問 | 回答 |
|---|---|
| ホットキー | 1 キーへの集中でノードが飽和 → ① クライアント側ローカルキャッシュ(短い TTL)② キーを複製(key#1..#N にランダム分散)③ 専用のレプリカ群 |
| ビッグキー | 数 MB の値がネットワークとメモリを圧迫 → 分割、圧縮、そもそもキャッシュしない |
| 追い出し | LRU が基本。スキャン耐性が必要なら W-TinyLFU / SLRU |
| キャッシュ一貫性 | TTL + 明示的削除。分散環境では削除が届かないケースがある → 短い TTL を保険にする |
| ノード障害時 | コンシステントハッシュにより担当キーが隣へ移る → そのキーは全ミスになる。DB への負荷急増に備えてレート制限 |
| リバランス中の一貫性 | 移行中のキーが「新旧どちらにあるか」の問題 → 移行期間は両方を読む、またはスロット単位で明示的に移行(Redis Cluster 方式) |
| なぜ単一スレッドで速いのか(Redis) | すべてメモリ上、ロック不要、コンテキストスイッチなし。ボトルネックは CPU ではなくネットワーク I/O。だから I/O だけマルチスレッド化された |
Q 14メトリクス・監視システム(Prometheus / Monarch)★★★★解答を開く
難易度: ★★★★
14-1. 要件
機能: ①メトリクス収集 ②時系列の保存 ③クエリと集計 ④アラート ⑤ダッシュボード
非機能: 1000 万時系列、10 秒間隔(= 100 万点/秒の書き込み)、
1 年保持、クエリ p99 < 1 秒
14-2. 見積もり
1000 万時系列 × 6 点/分 = 6000 万点/分 = 100 万点/秒
1 点 = (timestamp 8B + value 8B) = 16 B → 圧縮前 16 MB/s = 1.4 TB/日
→ **圧縮が必須**。Gorilla 圧縮で 1 点あたり ~1.4 B(約 10 倍)→ 140 GB/日
14-3. 時系列データの圧縮(Facebook の Gorilla 方式)
タイムスタンプ: デルタ・オブ・デルタ符号化
10:00:00, 10:00:10, 10:00:20, 10:00:30 → 間隔が一定なら差分の差分が 0
→ ほぼ 1 ビットで表現できる
値: XOR 符号化
連続する値は似ている → 前の値と XOR すると先頭と末尾に 0 が並ぶ
→ 有意なビットだけを記録
結果: 1 点あたり平均 1.37 バイト(元の 16 B から 12 倍圧縮)
🎤 この仕組みを説明できると、時系列 DB の理解として高評価です。
14-4. アーキテクチャ
【収集】
Pull 型(Prometheus): サーバーが各ターゲットの /metrics を定期取得
✅ ターゲットの死活が分かる、設定が中央集約
❌ NAT 越え・短命なジョブに弱い(Push Gateway で補う)
Push 型(StatsD/OTLP): クライアントが送る
✅ 短命なジョブ・サーバーレスに対応
❌ 送信元の暴走で受け側が死ぬ(レート制限が必要)
【保存】
直近(数時間): メモリ上の write-ahead + 可変長ブロック
中期: ローカル SSD の TSDB(時間ブロック単位、インデックス付き)
長期: オブジェクトストレージ(Thanos/Cortex/Mimir 方式)+ ダウンサンプリング
【クエリ】
ラベルの転置インデックス(label=value → 時系列 ID のポスティングリスト)
→ 集合演算で対象時系列を絞る → 該当ブロックを読む → 集計
14-5. 深掘り
| 質問 | 回答 |
|---|---|
| カーディナリティ爆発 | ラベルに user_id を入れると時系列が爆発 → 取り込み時に上限を設けて拒否、高カーディナリティはログ/トレースへ |
| ダウンサンプリング | 生データは 15 日、5 分粒度は 90 日、1 時間粒度は 1 年。古いデータは粗く |
| アラートの評価 | 全アラートルールを定期評価するのは重い → ルールをシャーディング、評価結果もメトリクスとして記録 |
| 高可用性 | 収集を 2 系統冗長化し、クエリ時に重複排除(Thanos の deduplication) |
| グローバル集計 | リージョンごとにローカル集計し、上位層でマージ(階層集約)。🔬 Google の Monarch はこの階層構造をリージョン→グローバルで持つ |
| なぜ汎用 DB でなく専用 TSDB か | 書き込みが追記のみ・時刻順、クエリが時間範囲、値が圧縮しやすい、古いデータの一括削除。これらに特化することで 100 倍の効率が出る |
Q 15広告配信とクリック集計★★★★解答を開く
難易度: ★★★★
15-1. 要件
機能: ①広告のターゲティングと配信 ②インプレッション/クリックの記録 ③リアルタイム集計
④予算管理(**予算超過で配信を止める**) ⑤課金
非機能: 広告選択 p99 < 50 ms、1 日 1000 億イベント、
課金に使うので**集計は正確でなければならない**、不正クリックの検出
15-2. 見積もり
イベント 1000 億/日 = 100 万 QPS
1 イベント 200 B → 20 TB/日
→ 生イベントの保存は安いストレージ、集計はストリーム処理
15-3. 配信パス(低レイテンシが命)
[広告リクエスト] → [広告サーバー]
① ターゲティング(ユーザー属性・文脈でマッチする候補を取得)… 数万件
② フィルタ(予算切れ、フリークエンシー上限、ブランドセーフティ)
③ 予測(CTR 予測モデル)… 数千件 → 上位数十件
④ オークション(eCPM = bid × pCTR で順位付け、セカンドプライス)
⑤ 配信 + インプレッションログ
すべて 50 ms 以内 → **候補生成は事前計算されたインデックスから、モデルは軽量**
15-4. 集計パス(正確性が命)
[クリックイベント]
│ クライアント → エッジ(軽量な受け口。書くだけ)
▼
[Kafka](パーティションキー = ad_id or campaign_id)
├──► 【リアルタイム経路】Flink/ストリーム集計
│ 1 分ウィンドウで集計 → Redis/OLAP へ → ダッシュボード(暫定値)
│ 予算消化の監視 → 閾値超過で配信停止シグナル
│
└──► 【バッチ経路】S3 に生ログ保存 → 日次バッチで正確に再集計
→ 課金はこちらの数値を使う(**リアルタイムは目安、課金は確定値**)
📐 これは Lambda アーキテクチャの正当な使用例です。 「速報値」と「確定値」で求められる性質が本質的に違うため、2 経路が正当化されます。
15-5. 深掘り
| 質問 | 回答 |
|---|---|
| 重複クリックの排除 | (user_id, ad_id, 時間窓) のキーで重複排除。規模が大きいので Bloom フィルタ + 正確な照合の 2 段 |
| 予算超過の防止 | 分散環境では完全には防げない → 各配信ノードに予算を分割配分し、消費が進んだら中央から再配分。わずかな超過分は事業判断で許容(オーバーデリバリー) |
| 不正クリック | ボット検知(IP、UA、クリック間隔、マウス軌跡)、閾値ルール、ML モデル。疑わしいものは「保留」として課金対象から除外 |
| イベントの遅延 | モバイルからのイベントは数時間遅れる → イベント時刻で集計 + ウォーターマーク(第 38 章)。遅延データは日次バッチで補正 |
| 正確な一意ユーザー数 | HyperLogLog で近似(ダッシュボード用)+ 課金対象は正確に集計(バッチ) |
| ホットキー | 人気キャンペーンのカウンタが 1 パーティションに集中 → キーをサフィックスで分割し 2 段集計 |
| ユーザープライバシー | サードパーティ Cookie の廃止に伴い、コンテキスト広告・オンデバイス処理・差分プライバシーへ移行 |
Q 16チケット予約システム(Ticketmaster / 座席指定)★★★★(在庫の一貫性が主題)解答を開く
難易度: ★★★★(在庫の一貫性が主題)
16-1. 要件
機能: ①イベント/座席の検索 ②座席の仮押さえ ③決済 ④確定
非機能: **同じ座席を 2 人に売らない(絶対)**、
人気公演の発売開始時に 100 万人が同時アクセス、
仮押さえは 10 分で自動解放
📐 この問題の本質: 「読み取りは大量・結果整合で良い」が 「在庫の確定だけは厳密な一貫性が必要」という、性質の異なる 2 つが同居すること。
16-2. 設計
【閲覧(大量・結果整合)】
[Client] → [CDN] → [イベント情報 API] → [キャッシュ] → [DB レプリカ]
座席の空き状況は「数秒古くてよい」→ キャッシュして大量アクセスを捌く
※ ただし「空いてると思ったら埋まっていた」体験を減らす工夫は必要
【仮押さえ(厳密)】
[Client] → [予約サービス] → [予約 DB(RDB のトランザクション)]
BEGIN;
SELECT status, held_by, hold_token, hold_expires_at FROM seats WHERE event_id=? AND seat_id=? FOR UPDATE;
-- または条件付き更新(こちらが軽い)
UPDATE seats SET status='held', held_by=?, hold_token=?, hold_expires_at=now()+interval '10 min'
WHERE event_id=? AND seat_id=? AND (status='available'
OR (status='held' AND hold_expires_at < now()));
-- 更新行数 0 → 他の人に取られた(409 を返す)
COMMIT;
【確定】決済成功 → `status='sold'` に更新。ただし `event_id`、`seat_id`、`held_by`、`hold_token`、期限、現在状態を条件に含める。
古い決済通知は更新行数 0 として無視・照合する(Saga: 決済失敗なら hold を解放)
【期限切れ】バックグラウンドジョブ or 参照時の遅延評価で 'available' に戻す
16-3. 発売開始の殺到(サンダリングハード)への対策
【仮想待合室(Virtual Waiting Room)】★ 有力な選択肢
① 発売前にアクセスした全員をキューに入れる(トークンを発行)
② 一定レート(例: 1000 人/分)でのみ本システムへ入場を許可
③ 待機中は「あなたは 12,345 番目です」と表示(体験も改善する)
→ バックエンドへの流入を制御できる。ただしキュー、レート制限、在庫の条件付き更新、Bot対策、容量増強を組み合わせる
【その他】
・座席マップは静的化して CDN へ(在庫状態だけ別途軽量 API で取得)
・書き込みパスを極限まで軽くする(決済は仮押さえの後)
・Bot 対策(CAPTCHA、レート制限、購入枚数制限)
16-4. 深掘り
| 質問 | 回答 |
|---|---|
| なぜ NoSQL でなく RDB? | 座席の確保は「複数行にまたがる強い一貫性」が必要(連番の隣接席 4 枚など)。イベント単位でシャーディングすれば、1 イベントの在庫は 1 ノードに収まる |
| シャーディング | event_id でシャード。人気イベントが 1 ノードに集中する → そのイベント専用ノードを割り当てる(ディレクトリ方式) |
| ロックの粒度 | イベント全体をロックすると同時実行できない。座席単位の行ロックにする。連番席は座席 ID 順にロックしてデッドロックを回避 |
| 仮押さえの期限管理 | 定期ジョブは遅延が出る → 参照時に hold_expires_at < now() を条件に含める(遅延評価)ことで、ジョブ遅延に依存しない設計にする |
| 一般席(座席指定なし) | 個別の座席ではなくカウンタの減算。UPDATE ... SET remaining = remaining - 1 WHERE remaining >= 1 で原子的に。さらに高速化するならカウンタを N 分割 |
| 決済失敗時 | Saga で hold を解放。ユーザーには即座に通知。決済中は座席を確保したままにする(決済してから在庫確認は最悪) |
| 転売対策 | 本人確認、購入枚数制限、電子チケットの動的 QR |
16-5. まとめ
「このシステムの本質は、閲覧(大量・結果整合可)と在庫確保(少量・厳密)の分離です。 閲覧は CDN とキャッシュで捌き、在庫確保だけを RDB のトランザクションで守ります。 在庫確保の QPS は座席数に上限があるため、実は大した量にはなりません。 発売開始の殺到に対しては、仮想待合室で入場レートそのものを制御し、 バックエンドの処理能力、キューの滞留時間、SLOを基準に流入を制御し、能力を超えないようにします。」
Q 17セマンティック検索 & RAG(検索拡張生成)システム★★★(Google / OpenAI 等で最頻出)解答を開く
難易度: ★★★(Google / OpenAI 等で最頻出)
17-1. 要件
機能要件:
・社内ドキュメント(1,000 万件、平均 10 KB)をアップロード・自動インデックス
・ユーザーの自然文の質問に対し、関連文書を検索し、根拠のある回答をストリーミング生成
・ドキュメント単位のアクセス権(ACL)制御
・ドキュメント更新/削除が 1 分以内に検索へ反映
非機能要件:
・検索 + 回答生成のレイテンシ: TTFT(最初のトークン)< 300 ms、全体完了 p99 < 2 秒
・検索 QPS: 500 QPS(ピーク 1,500 QPS)
・ハルシネーションの最小化(根拠のない回答をしない、引用リンクを付与)
17-2. 見積もり
データ量:
・ドキュメント 1,000 万件 × 10 KB = 100 GB(テキスト)
・チャンキング: 500 トークン(約 2 KB)/ チャンク(オーバーラップ 10%)→ 5,000 万チャンク
・ベクトルストレージ: 5,000 万 × 1,536 次元 × 4 B (float32) = 300 GB
・HNSW インデックス RAM: ベクトル 300 GB + グラフエッジ 150 GB = 450 GB
→ 64 GB RAM ノード × 8 台のベクター DB クラスタ(IVF-PQ なら 1 台 40 GB に圧縮可能)
計算リソース:
・クエリ Embedding: ピーク 1,500 QPS × 10 ms は、Little の法則では平均同時実行数約 15 を表す。
GPU台数は、モデル、バッチサイズ、実測throughput、p95/p99制約、冗長性から決める
・LLM 回答生成: キャッシュヒット率を仮定するだけでなく、入力/出力トークン、同時ストリーム数、モデルthroughputから容量を見積もる
17-3. API
POST /v1/documents
Request: {doc_id, title, content, acl_groups: ["eng", "hr"], metadata: {created_at, dept}}
Response: 202 Accepted {task_id}
POST /v1/chat/completions
Request: {query: "育児休業の手続き方法は?", conversation_id, stream: true}
Response: Server-Sent Events (SSE)
data: {"token": "育児", "citations": []}
...
data: {"token": "", "citations": [{"doc_id": "hr-102", "title": "育休ガイド.pdf"}]}
17-4. アーキテクチャ
【インジェスチョン(文書登録)パイプライン】
[Document Upload] → [Kafka] → [Chunking Service] (500 tokens / 10% overlap)
│
┌────────────────────┴────────────────────┐
▼ ▼
[Embedding Worker (GPU)] [Elasticsearch (BM25)]
│ │
▼ ▼
[Vector DB (HNSW / Milvus)] [Metadata DB (PostgreSQL)]
(ベクトル + ACL ビットセット) (文書本文 + ACL 定義)
【クエリ(質問・生成)パイプライン】
[Client] ──(SSE)──► [API Gateway]
│
▼
[認証・テナント/ACLコンテキスト確定]
│
▼
[Semantic Cache (Redis)] ──(権限・モデル・コーパス版を含むキー)──► 安全な場合だけ回答返却
│ ミス
▼
[Query Rewriter / HyDE] (検索用クエリの最適化)
│
┌──────────────┴──────────────┐
▼ ▼
[Embedding 推論] [BM25 キーワード検索]
│ │
▼ ▼
[Vector DB (HNSW)] [Elasticsearch]
(ACL フィルタ適用) (ACL フィルタ適用)
└──────────────┬──────────────┘
▼
[RRF ランク統合] (上位 50 件)
│
▼
[Cross-Encoder Reranker] (上位 5 件を厳選)
│
▼
[Prompt Assembler] (システム指示 + 厳選チャンク + 質問)
│
▼
[LLM 推論エンジン] ──(Streaming)──► [Client]
17-5. 深掘り
| 論点 | 対策 |
|---|---|
| ハルシネーション防止 | ① Cross-Encoder のスコアが閾値(例: 0.7)未満なら「社内文書に該当情報がありません」と回答。② プロンプトに「提供されたコンテキストのみに基づいて回答せよ。推測するな」を強制。③ 全回答に [doc_id] の引用リンクを付与 |
| ACL(権限)の高速評価 | 認証・認可コンテキストを検索前に確定し、検索候補、再ランキング、キャッシュの全段で適用する。インデックス内フィルタだけで漏洩を完全に防げるとは仮定せず、deny-by-default、監査、テナント分離、削除反映を検証する |
| ドキュメント更新・削除 | 文書更新時は旧チャンクに is_deleted=true をマークし、新チャンクを挿入。夜間バッチで Vector DB のインデックス最適化(Compaction)を実行 |
| 長いコンテキスト対策 | 検索されたチャンクから無関係な文を削除する Context Compression(LLMLingua 等)を挟み、LLM のプロンプト長を最小化してレイテンシとコストを半減 |
17-6. まとめ
「検索品質と精度のために BM25 とベクトル検索を組み合わせ、RRF とリランカーを導入しました。 認証後のテナント・権限コンテキストを検索、キャッシュ、プロンプト生成の全段で検証し、 文書更新・削除、プロンプトインジェクション、引用の正しさを評価します。権限漏洩を完全に防ぐとは断定せず、監査と負荷試験で確認します。」
Q 18大規模レコメンデーションシステム(YouTube / TikTok 型)★★★(Google / Meta で最頻出)解答を開く
難易度: ★★★(Google / Meta で最頻出)
18-1. 要件
機能要件:
・ユーザーごとにパーソナライズされた動画フィードを推薦
・ユーザーの直近のリアクション(視聴完了、いいね、スキップ)が 1 秒以内に次回の推薦に反映される
非機能要件:
・規模: DAU 2 億人、総動画数 10 億本
・フィード取得レイテンシ: p99 < 50 ms
・スループット: ピーク 25 万 QPS
・コールドスタート問題(新規ユーザー・新規動画)の解決
18-2. 見積もり
QPS:
・2 億人 × 50 回/日 ÷ 10^5 ≒ 100,000 QPS(平均)、ピーク 250,000 QPS
ストレージ:
・動画埋め込み: 10 億本 × 128 次元 × 4 B = 512 GB → ScaNN インデックスで 16 ノードに分散
・オンライン特徴量: 2 億人 × 5 KB = 1 TB → Redis / Bigtable クラスタ
特徴量アクセス:
・25 万 QPS × 1,000 候補 = 2.5 億 lookup/s → バッチ Multi-Get とローカルキャッシュで処理
18-3. 3 段階推薦パイプライン
[Client] ──(GET /v1/feed)──► [Recommendation Gateway]
│
┌───────────────────────────────┴───────────────────────────────┐
▼ ① Candidate Generation (Retrieval) [~15 ms] │
・ユーザー ID → オンライン特徴量ストア (Redis) から直近視聴 5 件を取得 │
・User Tower モデル (GPU) でユーザーベクトル u を計算 │
・ScaNN / HNSW クラスタで ANN 探索: 10 億本 → 1,000 本抽出 │
└───────────────────────────────┬───────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────┐
▼ ② Heavy Ranking (Scoring) [~25 ms] │
・1,000 候補の特徴量(動画静的特徴 + ユーザーリアルタイム特徴)を │
Feature Store から Multi-Get で一括取得 │
・DLRM / Transformer ランキングモデル(GPU)で各動画の CTR と │
視聴時間を予測スコアリング │
└───────────────────────────────┬───────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────┐
▼ ③ Re-ranking & Business Rules [~5 ms] │
・作者・カテゴリの重複排除 (MMR) │
・Exploration Slot (5%): 新規動画・新規カテゴリの探索 │
・広告挿入・ネガティブフィードバック(ブロック・報告済み)の除外 │
└───────────────────────────────┬───────────────────────────────┘
▼
上位 20 本の動画リストを返却(トータル 45 ms)
18-4. データモデルと特徴量ストア
【Feature Store の二重構造】
・オフライン (BigQuery / Dataflow):
過去 1 年の全イベントログから、動画ごとの 7 日間平均視聴時間、CTR を集計。
毎日夜間にバッチモデル学習を実行。
・オンライン (Bigtable / Redis):
user:{id}:recent_actions → [ {video_id, action, timestamp} ] (TTL 24h)
video:{id}:stats → {views_1h, likes_1h, shares_1h} (Flink で 1 分集計)
18-5. 深掘り
| 論点 | 対策 |
|---|---|
| コールドスタート | 新規ユーザー: 人気ランキング + 地域トレンド + オンボーディング時のジャンル選択で初期化。<br>新規動画: タイトル・画像・音声からアイテム Tower だけで即座にベクトル生成し、推薦枠の 5%(Exploration Slot)に強制注入して初期 CTR を計測 |
| 位置バイアス (Position Bias) | 画面の位置はクリック確率に影響する。ランダム化ログ、クリックモデル、propensity score / inverse propensity weightingなどで補正し、オフライン評価とオンライン実験を分ける。推論時に全件 position=1 とするだけでは補正にならない |
| リアルタイムフィードバック | ユーザーのタップ/スキップイベントを Kafka へ送信 → Apache Flink でリアルタイム集計し、500 ms 以内に Redis のオンライン特徴量を更新 |
18-6. まとめ
「10 億本から 50 ms でパーソナライズ推薦を行うため、Two-Tower による ANN 候補抽出(1,000 件)、DLRM による精密ランキング(100 件)、多様性制御の Re-ranking(20 件)という 3 段階パイプラインを採用しました。 Flink と Redis によるリアルタイム特徴量ストアで直近のアクションを即座に反映し、 位置バイアスの補正と探索枠(Exploration)によりコールドスタートとエコーチェンバーを打破する構成です。」
Q 19大規模 LLM 推論サービング基盤(Gemini / ChatGPT 型)★★★(最新インフラ・差別化問題)解答を開く
難易度: ★★★(最新インフラ・差別化問題)
19-1. 要件
機能要件:
・自然文プロンプトを受け取り、LLM による回答をストリーミング生成
・マルチターン対話(会話履歴のコンテキスト維持)
・優先度付きキューイングとトークン単位のレート制限
非機能要件:
・モデル: 70B パラメータ(FP16 で 140 GB、INT8 量子化で 70 GB)
・スループット: ピーク 2,000 QPS(同時アクティブ対話 20,000 セッション)
・レイテンシ: TTFT(Time To First Token)< 200 ms、生成速度 > 30 tokens/秒/ユーザー
・GPU メモリ利用効率の最大化
19-2. 見積もり
モデルメモリ:
・70B (INT8): 重みだけで概算 70 GB。実際には KV Cache、ランタイム、ワークスペース、断片化の余裕が必要で、80 GB GPU 1 枚に安全に収まるとは限らない
KV Cache メモリ:
・1 セッション(プロンプト 2,000 + 生成 1,000 = 3,000 トークン):
KV bytes = 2 × 80層 × KVヘッド数 × head_dim × 3000 × 2B
例: GQA(KVヘッド8、head_dim 128)なら約 0.98 GB(10進)。MHA(8192次元)なら約 7.86 GB。
使うモデルのKVヘッド数を必ず確認する
・1 GPU ノード(80 GB H100 × 2 = 160 GB):
モデル 70 GB + バッファ 20 GB → 残り 70 GB を KV Cache に使用
→ ノードあたり同時リクエスト数は、残りVRAM、KVサイズ、出力長、バッチング、TTFT/TPOT目標を実測して決める
クラスタ規模:
・同時 20,000 セッション ÷ 50 セッション/ノード = 400 GPU ノード(計 800 基の H100)
19-3. アーキテクチャ(Prefill / Decode 分離 + PagedAttention)
[Client] ──(HTTP/3 / SSE)──► [Global Load Balancer (Envoy)]
│
▼
[Distributed LLM Router]
(トークンレート制限 / セッションルーティング)
│
┌───────────────────────────┴───────────────────────────┐
▼ 【Prefill クラスタ (Compute-bound)】 │
・プロンプト全体(2,000 トークン)を一括並列処理 (TTFT < 150ms) │
・初期 KV Cache を生成 │
└───────────────────────────┬───────────────────────────┘
│ RDMA (400 Gbps ネットワーク)
▼
┌───────────────────────────────────────────────────────┐
▼ 【Decode クラスタ (Memory Bandwidth-bound)】 │
・Continuous Batching: 毎ステップで新規/完了を差し替え │
・PagedAttention (vLLM): 不連続なブロック単位で KV Cache 管理│
・Prefix Caching: 共通システムプロンプトの KV を再利用 │
・1 トークン生成ごとに即座に Gateway 経由で SSE ストリーム │
└───────────────────────────────────────────────────────┘
19-4. 核心技術
| 技術 | 解決する課題 |
|---|---|
| PagedAttention | KV Cacheを固定ブロックで管理し、内部断片化を減らせる。メモリ利用率とスループットは実装、ベースライン、ワークロードに依存する |
| Continuous Batching | 生成完了リクエストをステップごとに入れ替える。スループットは出力長、バッチ、スケジューラ、モデルに依存する |
| Prompt (Prefix) Caching | 同一プレフィックスを再利用できる。テナント、権限、モデル版、システムプロンプト、データ鮮度が同じ場合だけ共有し、削減率は実測する |
| 投機的デコーディング | ドラフトの受理率と検証コストに応じて速度が変わる。特定の倍率を一般保証しない |
19-5. 深掘り
| 論点 | 対策 |
|---|---|
| GPU ノード障害 | Decodeノード障害時は、生成済みトークン履歴から再計算するか、検証済みのKVチェックポイントを使う。prefill時点のKVをそのまま最新状態として再開できるとは限らない |
| 長いコンテキストの OOM | CPU/NVMeへのKVオフロードは転送遅延と帯域コストが大きい。全履歴attentionでは、スライディング窓、圧縮、再計算などと合わせて評価する |
| トークン単位のレート制限 | リクエスト数ではなく、TPM (Tokens Per Minute) を Redis のトークンバケットで管理。巨大プロンプトによるクラスタ枯渇を防止 |
19-6. まとめ
「70B LLM の大規模推論基盤として、GPU 計算律速な Prefill と VRAM 帯域律速な Decode をクラスタレベルで分離し、RDMA で KV Cache を転送するアーキテクチャを設計しました。 PagedAttention と Continuous Batching により GPU メモリ利用率とスループットを極限まで高め、 Prefix Caching と投機的デコーディングを組み合わせて TTFT 200 ms 以下と 30 tokens/秒以上の高速生成を両立させています。」
19-7. 追加課題:業務ツールを操作する AI エージェント
要件例: ユーザーの依頼から社内検索、チケット照会、下書き作成を行う。送信・削除・権限変更は 明示的な承認が必要。複数ツールの一部がタイムアウトしても、重複操作や越権を起こさず再開できること。
設計で必ず触れる論点:
| 論点 | 最低限の設計 |
|---|---|
| ワークフロー選択 | 固定手順はステートマシン、探索が必要な部分だけエージェントにする |
| ツール契約 | 型付き引数、期限、冪等キー、read-only/副作用分類、状態照会、事後条件 |
| 状態と再開 | task_id、ステップ、試行回数、lease、チェックポイント、監査イベントを永続化 |
| 権限 | ユーザー・テナント・リソース単位で能力を絞り、モデル出力を認可の根拠にしない |
| 安全確認 | 送信・削除は dry-run → 変更差分 → 人間承認 → 実行。承認の期限と対象を固定 |
| 注入対策 | 検索結果・メール・ツール応答を untrusted input として分離し、秘密情報・外部 URL を制限 |
| 評価と運用 | 軌跡の成功率、ツール選択・引数、ACL違反、不要呼び出し、費用、p99、引き継ぎ率を測定 |
深掘り質問: 「決済 API がタイムアウトした後、ユーザーが再送したら二重請求されないか」
に対し、idempotency_key、状態照会、事後条件、期限付きリース、監査トレースを組み合わせて説明します。
演習問題の総まとめ:頻出パターン早見表
| パターン | 使う場面 | 出てきた問題 |
|---|---|---|
| 事前計算(ファンアウト) | 読みが書きより遥かに多い | Twitter, オートコンプリート |
| ハイブリッド(Push/Pull) | 分布が偏っている(セレブ・人気商品) | |
| ステートフル層の分離 | 永続接続を扱う | チャット |
| CDN 中心設計 | 大量の静的/準静的コンテンツ配信 | 動画, URL 短縮 |
| チャンク化 + 重複排除 | 大きなファイル、差分同期 | Drive |
| インメモリ + 非同期永続化 | 高頻度・短寿命の書き込み | Uber の位置情報 |
| 一貫性レベルの分離 | 大半は緩く、一部だけ厳密 | Uber, チケット, 決済 |
| 冪等性キー + 状態機械 | 二重実行が許されない | 決済, ジョブスケジューラ |
| 追記専用台帳 | 監査可能性が必要 | 決済 |
| 速報値 + 確定値の 2 経路 | 速さと正確さの両方が必要 | 広告集計 |
| 仮想待合室 | 人間側が同期して殺到する | チケット |
| 確率的データ構造 | 概算で良く、規模が巨大 | クローラ, 広告, 分析 |
| 地理インデックス + セル分割 | 位置情報の近傍検索 | Uber |
| オフライン構築 + メモリ常駐 | 極端に厳しいレイテンシ要件 | オートコンプリート |
| 能力制限付きツール + 状態機械 | LLM が外部操作し、途中失敗・承認・再開がある | AIエージェント |
Google の実システムを読む
Googleの実システムから設計思想の原典を学ぶ
GFS / Bigtable / Spanner / Borg の名前を暗記するのではなく、なぜその設計になったのかを読む章です。
🎯 この章のゴール: Google 面接で「そのアイデアの原典」を知っている状態になる。
⚠️ 注意: 面接でこれらの名前を並べるだけでは加点されません。 「なぜその設計になったか」を自分の言葉で説明できることが価値です。
48.1 ストレージ系
| システム | 論文年 | 何を解いたか | 本書での関連 |
|---|---|---|---|
| GFS / Colossus | 2003 | 安価なマシンで PB 級の分散ファイルシステム。大きなチャンク(64 MB)、追記中心、単一マスター + チャンクサーバー。Colossus は後継でメタデータを分散化 | 第 23 章 |
| Bigtable | 2006 | ソート済みの疎な多次元マップ。行キーでソート、タブレット分割、SSTable + LSM-Tree、Chubby でマスター管理 | 第 11, 14, 16 章 |
| Spanner | 2012 | グローバル分散 SQL + 外部一貫性。Paxos によるレプリケーション、TrueTime による commit-wait、2PC を Paxos グループ間で | 第 17, 19, 39 章 |
| Megastore | 2011 | Bigtable の上にエンティティグループ単位の ACID | 第 13 章 |
| Chubby | 2006 | Paxos ベースのロックサービス。小さいが極めて重要なメタデータの置き場。ZooKeeper の原型 | 第 19 章 |
| Percolator | 2010 | Bigtable 上の増分処理と分散トランザクション。検索インデックスをバッチから増分更新へ | 第 18 章 |
📐 GFS/Bigtable から学ぶ最大の教訓:
「安いハードウェアは壊れる。壊れることを前提に、ソフトウェアで信頼性を作る。」 これが Google のインフラ思想の原点です。
48.2 計算・データ処理系
| システム | 何を解いたか |
|---|---|
| MapReduce (2004) | 分散並列処理を Map と Reduce という 2 つの関数に抽象化。耐障害性を自動化(失敗したタスクだけ再実行) |
| Dremel (2010) | 列指向 + ツリー状の集約で、PB 級を秒でクエリ。BigQuery の基盤。ネストしたデータの列化(repetition/definition level)が肝 |
| FlumeJava / Dataflow / Beam (2010, 2015) | バッチとストリームを統一したモデル。ウォーターマーク・ウィンドウ・トリガーという時間モデルを定式化 |
| Borg → Kubernetes (2015) | クラスタ全体を 1 台のコンピュータのように扱う。リソースのビンパッキング、優先度とプリエンプション、宣言的な望ましい状態 |
48.3 ネットワーク・分散基盤
| システム | 何を解いたか |
|---|---|
| Maglev (2016) | ソフトウェア L4 ロードバランサ。ECMP + コンシステントハッシュで、ノード増減時の接続断を最小化 |
| Jupiter | データセンタ内ネットワーク。Clos トポロジで任意のサーバー間がフル帯域 → 「配置を気にしなくてよい」設計自由度を生む |
| Espresso / GFE | エッジでの TLS 終端とグローバルロードバランシング |
| Zanzibar (2019) | グローバルな認可システム。関係ベース (ReBAC) のタプルで「誰が何にアクセスできるか」を表現。一貫性のために Zookie(タイムスタンプトークン)で「この時点以降の状態で判定せよ」を実現 |
| Dapper (2010) | 分散トレーシング。低オーバーヘッドのサンプリングと Trace ID の伝播 |
| Monarch (2020) | インメモリの時系列監視システム。リージョナル → グローバルの階層構造で、リージョン障害時も自リージョン内で監視が機能する |
48.4 面接で語れる「思想」
これらのシステムに共通する設計思想を、面接で言語化できると強力です。
📐 ① 失敗を前提にする
「1 万台あれば毎日何台かは壊れます。だから個々のハードの信頼性ではなく、 ソフトウェアによる複製と自動復旧で信頼性を作ります。」
📐 ② シンプルな抽象を作り、複雑さをそこに閉じ込める
MapReduce、Borg、Spanner はすべて「利用者に見せる抽象を単純に保ち、 難しい部分を基盤側が引き受ける」設計です。
📐 ③ 測定できないものは改善できない
Dapper と Monarch の存在自体が、「まず観測可能にする」という文化を示しています。
📐 ④ 物理法則には逆らわない、が、上限は保証する
TrueTime の発想(時計を正確にするのではなく、誤差の上限を保証して、その分待つ)は 「不確実性を排除するのではなく、境界を定めて扱う」という思考の見本です。
🎤 面接での使い方の例:
「この部分は Bigtable の設計に近い考え方を採ります。 行キーでソートして保持し、範囲でタブレットに分割することで、 範囲スキャンの局所性と、分割による水平スケールを両立させます。 ホットなタブレットは自動分割されるようにします。」
コーディング面接・OS・並行処理の最低限
コーディング・OS・並行処理で落ちない最低限を固める
🎯 この章のゴール: システムデザイン以外のラウンドで落ちないための最低限を押さえる。
システムデザインだけできても合格はできません。この章は要点の圧縮版です。 詳細は専門書と LeetCode での演習で補ってください。
49.0 この章の位置付け(コーディング対策の境界)
ここはシステムデザイン読者が見失いやすい計算量・データ構造・OS・並行処理の地図です。 この数ページだけでコーディングラウンドを合格できるという意味ではありません。Google SWE 全体を目標にするなら、 別途、選んだ言語で実装を反復し、テスト・デバッグ・説明・時間管理まで含む模擬ラウンドを行います。
最低限の到達ゲートは次の通りです。
- 代表パターンを暗記でなく、制約から選び、計算量を説明できる
- 30〜50 問程度の良質な問題を、複数回、異なる入力で実装し直せる
- 空入力、重複、境界、整数オーバーフロー、再帰深さ、並行実行を自分でテストできる
- 途中で詰まったときに、ブルートフォース、部分点、計算量の改善を口頭で説明できる
- 45 分の模擬を録音し、コードの正しさだけでなくコミュニケーションと時間配分を採点する
49.1 計算量
📐 必ず即答できるように:
O(1) < O(log n) < O(√n) < O(n) < O(n log n) < O(n²) < O(2^n) < O(n!)
n = 10^8 → O(n) が限界(1 秒)
n = 10^6 → O(n log n) が可能
n = 10^4 → O(n²) が可能
n = 20 → O(2^n) が可能
| データ構造 | 検索 | 挿入 | 削除 | 備考 |
|---|---|---|---|---|
| 配列 | O(n) / O(log n)(ソート済み) | O(n) | O(n) | キャッシュ効率が最良 |
| 動的配列 | O(n) | 償却 O(1) | O(n) | |
| 連結リスト | O(n) | O(1) | O(1) | キャッシュ効率が悪い |
| ハッシュテーブル | O(1) 平均 | O(1) | O(1) | 最悪 O(n)、順序なし |
| 平衡二分探索木 | O(log n) | O(log n) | O(log n) | 順序を保つ |
| ヒープ | O(1)(最小) | O(log n) | O(log n) | 優先度キュー |
| Trie | O(m)(m=語長) | O(m) | O(m) | プレフィックス検索 |
| Union-Find | ほぼ O(1) | — | — | 連結成分 |
49.2 必修アルゴリズムパターン(LeetCode 頻出)
□ 二分探索(答えを二分探索するパターンも含む)
□ 二方向ポインタ(Two Pointers)/ スライディングウィンドウ
□ ハッシュマップによる O(n) 化(Two Sum 型)
□ ソート + 貪欲(区間スケジューリング、マージ区間)
□ スタック(括弧、単調スタック = Next Greater Element)
□ BFS / DFS(グリッド、グラフ、木)
□ トポロジカルソート(依存関係、コース順)
□ ダイクストラ / Union-Find(最短経路、連結性)
□ 動的計画法(ナップサック、LCS、編集距離、区間 DP)
□ バックトラッキング(順列、組合せ、N クイーン、数独)
□ ヒープ(Top-K、K 個のソート済みリストのマージ)
□ 木(走査、LCA、直径、シリアライズ)
□ ビット演算(部分集合の列挙、XOR の性質)
📐 面接での進め方(コーディングラウンド):
① 問題を復唱し、例で確認する(**必ず具体例を作る**)
② エッジケースを列挙する(空、1 要素、重複、負数、オーバーフロー)
③ ブルートフォースの計算量を述べる
④ 改善案を述べ、計算量を宣言してから書く
⑤ 書きながら意図を説明する
⑥ 書き終えたら**自分で例をトレースして検証**する(バグの発見は加点)
⑦ 計算量(時間・空間)を最終確認する
⚠️ コードを書き始める前に、必ずアプローチの合意を取ること。 いきなり書き始めて間違った方向に 20 分使うのが最悪のパターンです。
49.3 OS の基礎(システムデザインに直結する部分だけ)
プロセスとスレッド
プロセス: 独立したメモリ空間。切替コストが高い(~数µs)。障害が隔離される
スレッド: メモリを共有。切替が軽い。共有ゆえに競合が起きる
コルーチン/グリーンスレッド: ユーザー空間でのスケジューリング。切替が極めて軽い(~ns)
→ Go の goroutine、Java の Virtual Thread、Python の asyncio
I/O モデル(C10K 問題の背景)
ブロッキング I/O + スレッド/接続 → 1 万接続で 1 万スレッド → メモリと切替で破綻
↓
ノンブロッキング I/O + イベント多重化(epoll / kqueue / io_uring)
→ 少数のスレッドで数十万接続を扱える(Nginx, Redis, Node.js)
📐 これが WebSocket サーバーが 1 台で 10 万接続を扱える理由です(第 10 章)。
メモリ
仮想メモリ / ページング / TLB
ページキャッシュ: ファイル I/O は OS がメモリにキャッシュする
→ DB が「自前のバッファプール」を持つ理由(OS より賢く管理したい)
mmap: ファイルをメモリ空間にマップ。ゼロコピーだが、ページフォルトの制御が難しい
ゼロコピー (sendfile): ユーザー空間を経由せずファイル → ソケット(Kafka が使用)
GC(ガベージコレクション)
Stop-The-World の停止時間がテールレイテンシの主因になる
→ 世代別 GC、G1/ZGC/Shenandoah(低停止時間)
→ メモリを増やすと GC 頻度は減るが、1 回の停止は長くなりうる
⚠️ 「GC 停止中はヘルスチェックにも応答しない」→ 誤ってノードが外される事故が起きる
49.4 並行処理
競合状態と対策
競合状態: 複数スレッドが同じデータを同時に読み書きし、順序で結果が変わる
対策:
ミューテックス(排他ロック)
読み書きロック(読みは並行可)
アトミック操作(CAS: Compare-And-Swap)
イミュータブル(そもそも変更しない)← 最も安全
スレッド閉じ込め(1 スレッドしか触らない)
デッドロックの 4 条件(すべて満たすと発生)
① 相互排除 ② 保持と待機 ③ 横取り不可 ④ 循環待ち
→ どれか 1 つを崩せば防げる。実務では **④ を崩す(ロック順序を統一)** が基本
知っておくべき概念
| 概念 | 意味 |
|---|---|
| ライブロック | 互いに譲り合って進まない |
| 飢餓 (starvation) | 特定のスレッドが永久に実行されない |
| 偽共有 (false sharing) | 別の変数が同じキャッシュラインにあり、無用な同期が発生して激遅になる |
| メモリバリア / happens-before | コンパイラと CPU が命令を並べ替えるため、明示的な順序保証が必要 |
| ロックフリー / ウェイトフリー | CAS ループで実装。高性能だが極めて難しい(ABA 問題) |
| アムダールの法則 | 直列部分が高速化の上限を決める(第 2 章) |
🎤 面接で聞かれる典型: 「複数スレッドから安全なカウンタをどう実装する?」 → アトミック(CAS)→ 競合が激しければ スレッドローカルに分割して集約(LongAdder 方式) → 分散環境なら Redis の INCR or シャード分割カウンタ、という流れで答えられると完璧です。
49.5 実装・検証の仕上げ
面接では、アルゴリズムを思いついた時点では終わりません。実装後に次を短時間で行えることが重要です。
① 不変条件を一文で言う(例: 各ノードを一度だけ訪問する)
② 小さい例、最小・最大・重複・不正入力を手でトレースする
③ 失敗するテストを一つ作り、修正後に再実行する
④ 時間・空間計算量と、入力制約に対する妥当性を確認する
⑤ 本番ならタイムアウト、キャンセル、再試行、競合、メモリ上限も確認する
言語固有の標準ライブラリ、整数型、ソートの安定性、スレッド安全性は、使用言語を一つ決めて 実際にコードを書き、コンパイラ・テスト・プロファイラで確かめます。
12 週間の学習ロードマップ
12週間で知識を面接パフォーマンスへ変換する
🎯 この章のゴール: 明日から何をすればいいかを明確にする。
50.1 全体計画
| 週 | システムデザイン | コーディング | その他 |
|---|---|---|---|
| 1 | 第 0〜4 章(基礎・見積もり) | 配列・文字列・ハッシュ(Easy 20 問) | — |
| 2 | 第 5〜10 章(ネットワーク) | 二分探索・双方向ポインタ(20 問) | — |
| 3 | 第 11〜14 章(DB 基礎) | 連結リスト・スタック(20 問) | 実際に PostgreSQL で EXPLAIN を触る |
| 4 | 第 15〜19 章(分散データ) | 木・BFS/DFS(25 問) | Raft の可視化サイトを触る |
| 5 | 第 20〜23 章(部品箱 前半) | グラフ・トポロジカルソート(20 問) | Redis と Kafka をローカルで動かす |
| 6 | 第 24〜27 章(部品箱 後半) | 動的計画法 基礎(25 問) | — |
| 7 | 第 28〜31 章(信頼性) | DP 応用・貪欲(25 問) | 自分のプロジェクトに監視を入れてみる |
| 8 | 第 32〜35 章(運用) | ヒープ・Top-K・区間(20 問) | — |
| 9 | 第 36〜43 章(アーキテクチャ・AI) | 総合演習(Medium 30 問) | 模擬面接 ×2。RAG / エージェントの評価設計 |
| 10 | 演習問題 1〜8 を紙で解く | 総合演習(Medium/Hard 30 問) | 模擬面接 ×2 |
| 11 | 演習問題 9〜16 を紙で解く | 弱点分野の集中演習 | 模擬面接 ×2、行動面接の準備 |
| 12 | 全体復習・弱点補強 | 総復習 | 体調管理、当日シミュレーション |
50.2 毎週の固定メニュー
□ 本書を 1 部読む(4〜6 時間)
□ LeetCode を 20〜25 問(10〜15 時間)
□ 演習問題を 1〜2 問、**45 分計測して紙に書く**(2 時間)
□ 自分の設計を録音して聞き直す(30 分)← 効果が非常に高い
□ 前週の弱点を 1 つ潰す
50.3 学習効果を最大化する 5 つのコツ
- 手を動かす: 図を描く。声に出す。読むだけの学習は面接では再現できません
- 実際に動かす: Redis / Kafka / PostgreSQL をローカルで動かし、 意図的に壊す(プライマリを kill する、キャッシュを消す)
- 説明する: 友人・同僚・(いなければ)鏡や録音に向かって説明する。 説明できない部分が、あなたが理解していない部分です
- 論文を 1 本読む: Dynamo、Bigtable、Raft のどれか 1 本を通読すると、 「本物の設計文書」の書き方が分かります
- 既存サービスを分解する: 使っているアプリ(LINE、Netflix、Amazon)が どう作られているかを想像し、本書の枠組みで説明してみる
50.4 参考文献(さらに深く学ぶために)
書籍(優先度順)
1. 『データ指向アプリケーションデザイン』(Martin Kleppmann) ★★★ 最重要
→ 第 3 部の内容を、圧倒的な深さで扱っています
2. 『Site Reliability Engineering』(Google, 無料公開) ★★★
→ 第 5 部の原典
3. 『Designing Data-Intensive Applications』の原書(上と同じ)
4. 『Database Internals』(Alex Petrov) — ストレージエンジンと分散
5. 『System Design Interview』(Alex Xu) Vol.1/2 — 演習量を増やしたいとき
6. 『Web Scalability for Startup Engineers』— 実務寄りの入門
論文(原典に触れる価値が高いもの)
・The Google File System (2003)
・MapReduce (2004)
・Bigtable (2006)
・Dynamo: Amazon's Highly Available Key-value Store (2007)
・The Chubby Lock Service (2006)
・Spanner (2012)
・In Search of an Understandable Consensus Algorithm (Raft, 2014)
・The Tail at Scale (2013) ← 短くて必読
・Kafka: a Distributed Messaging System for Log Processing (2011)
・Dapper (2010)
・Zanzibar (2019)
・Hidden Technical Debt in Machine Learning Systems (2015)
・Rules of ML(Google)
オンライン
・Google SRE Book / SRE Workbook(無料)
・AWS / Google Cloud Architecture Center のリファレンス構成
・各社のエンジニアリングブログ(Netflix, Uber, Discord, Cloudflare, Stripe)
→ 「実際にどう壊れて、どう直したか」が書かれており、極めて実践的
・raft.github.io(Raft の可視化)
・High Scalability(過去の実例アーカイブ)
・Building Effective AI Agents / Demystifying Evals for AI Agents(Anthropic)
50.5 面接前日・当日
【前日】
□ 新しいことを詰め込まない(第 3 章の数字と第 45 章の台本だけ確認)
□ 環境チェック(ネット、カメラ、共有エディタ、図を描くツール)
□ 早く寝る(睡眠不足は GCA スコアに直結します)
【当日】
□ 紙とペンを用意(画面共有でも手元でメモを整理する)
□ 水を用意
□ 最初の 5 分は必ず質問に使うと決めておく
□ 「分からない」と言う勇気を持つ
□ **楽しむ**。面接官は敵ではなく、一緒に問題を解く同僚です
用語集
直前復習用の用語集で説明力を点検する
面接直前の総復習用。定義を見て説明できるかを確認してください。
A. 基礎・性能
| 用語 | 定義 |
|---|---|
| レイテンシ | 1 件の処理にかかる時間 |
| スループット | 単位時間あたりの処理件数 |
| パーセンタイル (p99) | 上位 99% が収まる値。テールレイテンシの指標 |
| リトルの法則 | L = λW(並行数 = 到着率 × 滞在時間) |
| アムダールの法則 | 直列部分が並列化による高速化の上限を決める |
| テイルレイテンシ増幅 | 多数のサービス呼び出しで p99 が全体の体感を支配する現象 |
| ヘッジドリクエスト | 一定時間で返らなければ別レプリカにも要求を出す手法 |
B. ネットワーク
| 用語 | 定義 |
|---|---|
| RTT | 往復遅延時間 |
| Anycast | 同じ IP を複数拠点に割り当て、最寄りへルーティングする |
| L4 / L7 LB | トランスポート層 / アプリケーション層のロードバランサ |
| コンシステントハッシュ | ノード増減時の再配置を K/N に抑えるハッシュ手法 |
| Power of Two Choices | ランダム 2 台のうち空いている方を選ぶ負荷分散 |
| QUIC / HTTP/3 | UDP 上の信頼性 + TLS1.3。HOL ブロッキングを解消 |
| mTLS | 相互 TLS 認証。サービス間認証の標準 |
C. データ
| 用語 | 定義 |
|---|---|
| WAL | 先行書き込みログ。変更を先に順次書き込みで記録する |
| B-Tree | ページ単位の平衡木。読み取りが安定して速い |
| LSM-Tree | MemTable + SSTable + コンパクション。書き込みが速い |
| MVCC | 複数バージョンを保持し、読みと書きが互いをブロックしない |
| 分離レベル | READ COMMITTED / REPEATABLE READ / SERIALIZABLE |
| 書き込みスキュー | 個々は正しいのに同時実行で不変条件が壊れる異常 |
| 楽観ロック / 悲観ロック | バージョンで競合検出 / 事前に行をロック |
| シャーディング | データを分割して複数ノードに配置すること |
| クォーラム | W + R > N で最新値を読める複製の設定 |
| 線形化可能性 | 単一オブジェクトが 1 台のように振る舞う最強の一貫性 |
| 直列化可能性 | トランザクション群がある順序の逐次実行と等価 |
| CAP / PACELC | 分断時の C か A か / 平常時の L か C か |
| Raft / Paxos | 分散合意アルゴリズム。過半数で決定 |
| フェンシングトークン | 単調増加番号でゾンビノードの書き込みを拒否する仕組み |
| TrueTime | 時刻の不確実性上限を保証する Google の仕組み |
| Saga | ローカルトランザクションの連鎖 + 補償処理 |
| Outbox パターン | DB 更新とイベント発行を同一トランザクションで原子化 |
| CDC | DB の変更ログを読んで下流へ伝播する仕組み |
| 冪等性 | 同じ操作を何度実行しても結果の状態が同じ性質 |
D. 部品
| 用語 | 定義 |
|---|---|
| Cache-Aside | ミス時に読み込んでキャッシュに書き戻す戦略 |
| キャッシュスタンピード | 人気キーの失効で一斉に DB へ殺到する現象 |
| Bloom フィルタ | 「存在しない」を確実に判定する省メモリ構造 |
| HyperLogLog | 少メモリでユニーク数を近似する構造 |
| Count-Min Sketch | 頻度を近似する構造。過小評価しない |
| トークンバケット | バーストを許容するレート制限アルゴリズム |
| Snowflake ID | 時刻 + ノード ID + 連番の 64bit 分散 ID |
| Geohash / S2 / H3 | 2 次元の位置を 1 次元に落とす地理インデックス |
| 転置インデックス | 単語 → 文書 ID リストの索引 |
| BM25 | 現代の標準的な全文検索スコアリング |
E. 信頼性・運用
| 用語 | 定義 |
|---|---|
| SLI / SLO / SLA | 指標 / 社内目標 / 顧客との契約 |
| エラーバジェット | SLO の残余。リリース速度の判断に使う |
| RPO / RTO | 許容データ損失 / 許容復旧時間 |
| サーキットブレーカ | 失敗が続く依存先の呼び出しを遮断する仕組み |
| バルクヘッド | リソースプールを分離して障害を隔離する |
| 負荷制限 | 過負荷時に一部を早期に拒否して全滅を防ぐ |
| 指数バックオフ + ジッタ | リトライ間隔を指数的に伸ばし、ランダム化する |
| デッドライン伝播 | 絶対時刻の締切を下流へ渡す仕組み |
| メタスタブル障害 | 原因が消えても自力で復旧できない状態 |
| サンダリングハード | 大量のクライアントが同時に同じ動作をする現象 |
| シャッフルシャーディング | 顧客ごとに異なるノード組合せを割り当て影響を局所化 |
| ブラストレディウス | 障害の影響範囲 |
| カナリアリリース | 少数に先行展開して安全性を確認する |
| Expand-Contract | 拡張 → 移行 → 収縮の安全なスキーマ変更手順 |
| 4 大シグナル | レイテンシ・トラフィック・エラー・飽和度 |
| バーンレートアラート | エラーバジェットの消費速度で警告する方式 |
F. アーキテクチャ
| 用語 | 定義 |
|---|---|
| ステートレス | サーバーが状態を保持しない設計。水平スケールの前提 |
| ファンアウト | 1 つの書き込みを多数の宛先に配る処理 |
| CQRS | 書き込みモデルと読み取りモデルの分離 |
| イベントソーシング | 状態ではなく出来事の列を保存する方式 |
| ストラングラーフィグ | 段階的にレガシーを置き換える移行手法 |
| BFF | クライアント種別ごとの専用ゲートウェイ |
| セルベースアーキテクチャ | 独立したフルスタックの単位に分割し影響を限定 |
| ウォーターマーク | 「この時刻までのイベントは出揃った」という推定 |
| Lambda / Kappa | バッチ + ストリーム併用 / ストリームのみ |
| 暗号化消去 | 鍵の破棄をもって実質的な削除とする手法 |
G. AI / ML / エージェント
| 用語 | 定義 |
|---|---|
| Training-Serving Skew | 学習時と推論時で特徴量の計算・鮮度・意味がずれる問題 |
| Data Drift / Concept Drift | 入力分布が変化すること / 入力と正解の関係が変化すること |
| Two-Tower | ユーザー側とアイテム側を別々に埋め込み、候補検索を高速化するモデル構成 |
| Prefill / Decode | プロンプトを並列処理する段階 / 1 トークンずつ生成する段階 |
| KV Cache | 生成済みトークンの Key/Value を保持して再計算を省くメモリ |
| RAG | 外部文書を検索して LLM のコンテキストへ追加する構成 |
| AIエージェント | モデルが計画・ツール呼び出し・状態更新を繰り返す制御ループ |
| Tool Contract | ツールの型、権限、副作用、期限、冪等性、監査を定義する契約 |
| Agent Trajectory | モデル出力、ツール呼び出し、状態遷移、検証を含む一連の実行履歴 |
| Capability / Least Privilege | エージェントに公開する操作の範囲を必要最小限に絞る考え方 |
| Human-in-the-loop | 不可逆・高リスク操作を人間の確認後に実行する設計 |
おわりに
この本で伝えたかったこと
ここまで読み切ったあなたは、システムデザインに必要な語彙と道具をほぼすべて手にしています。 最後に、本書を貫いていた考え方を 5 つにまとめます。
① 数字から始める 「大量のアクセス」ではなく「ピーク 12,000 QPS」。 数字は設計を決定し、議論を客観的にし、あなたの主張に根拠を与えます。
② トレードオフしかない 一貫性と可用性、レイテンシとスループット、正確さとコスト。 「良い設計」とは、どれを捨てたかを説明できる設計です。
③ 単純さは正義である 部品を 1 つ足すたびに、可用性は下がり、運用コストは上がり、障害モードが増えます。 複雑な設計を提案する前に、常に「これは本当に必要か」と問うてください。
④ 壊れることを前提にする 必ず壊れます。ネットワークは切れ、ディスクは飛び、デプロイは失敗します。 「壊れないシステム」ではなく「壊れても被害が小さく、早く直せるシステム」 を作ってください。
⑤ 説明できることが実力である 面接でも実務でも、評価されるのは頭の中の知識ではなく、 声に出して説明し、図に描き、他人を納得させられることです。
最後に
システムデザインは暗記科目ではありません。 本書に書かれた「答え」を覚えるのではなく、 「なぜその答えになるのか」を再構成できるようになってください。 そうすれば、本書に載っていない問題が出ても、あなたは同じやり方で解けます。
面接で完璧である必要はありません。 必要なのは、曖昧な問題を構造化し、根拠を持って選び、指摘を受けて改善できる姿を見せることです。 それはまさに、実際の仕事で毎日やっていることです。
良い設計を。そして、良い結果を。
この本の使い方(再掲)
1 周目: 通読して地図を作る
2 周目: 各章の一問一答に答えられるか確認する
3 周目: 第 47 章の演習を 45 分計測で、紙とペンだけで解く
面接直前 30 分に見るべき 3 箇所
・第 3 章「覚えるべき数字」
・第 45 章「45 分の台本」とチェックリスト
・第 47 章 末尾「頻出パターン早見表」


