システム設計方法論
はじめに
システム設計はアドリブでアーキテクチャ図を描くことではなく、体系化された方法論に従うものです。 面接でのシステム設計問題であれ、実際の仕事でのアーキテクチャ設計であれ、似た思考フレームワークに従います。まず問題を明確にし、次に規模を見積もり、設計案を作成し、最後に深掘りして最適化します。
この記事で何を学べるのか?
この章を読み終えると、以下のことが身につきます:
- 設計プロセス:システム設計の4ステップフレームワークを習得する
- キャパシティ見積もり:「封筒の裏の計算」のテクニックを学ぶ
- 一般的なパターン:キャッシュ、シャーディング、メッセージキューなどのコアパターンに習熟する
- トレードオフ思考:アーキテクチャ設計におけるトレードオフの考え方を理解する
- 実践例:短縮URLサービス、フィードなどを使って設計プロセスを理解する
| 章 | 内容 | 核心概念 |
|---|---|---|
| 第1章 | 設計の4ステップ | 要件の明確化、キャパシティ見積もり、アーキテクチャ設計、深掘り最適化 |
| 第2章 | キャパシティ見積もり | QPS、ストレージ、帯域幅、封筒の裏の計算 |
| 第3章 | コア設計パターン | キャッシュ、シャーディング、メッセージキュー、CDN |
| 第4章 | トレードオフ思考 | 一貫性 vs 可用性、パフォーマンス vs コスト |
| 第5章 | 古典的な例 | 短縮URLサービス、フィード、フラッシュセールシステム |
1. システム設計の4ステップ
システム設計は、最初からアーキテクチャ図を描くことではありません。面接でも実践でも、構造化されたプロセスに従うべきです。
なぜまず要件を明確にするのか?
多くの人が問題を受け取った直後に図を描き始め、「正しいが面接官が求めていない」システムを設計してしまいます。5分かけて要件を明確にすれば、その後の30分のやり直しを防げます。
よくある明確化の質問:
- システムのコア機能は何か?(すべての機能を設計しない)
- ユーザー規模はどのくらいか?(分散の必要性を決める)
- 読み書きの比率は?(キャッシュ戦略を決める)
- データはどのくらい保持するか?(ストレージの設計を決める)
2. キャパシティ見積もり:封筒の裏の芸術
「封筒の裏の計算」(Back-of-envelope estimation)は、システム設計におけるコアスキルです。正確な計算は必要なく、オーダーが分かればよいのです。
よく使う換算早見表
| オーダー | 換算 | 覚え方 |
|---|---|---|
| 1日 | 86,400秒 | ≒ 約10万秒 |
| 1億リクエスト/日 | ≒ 1,200 QPS | 10万で割る |
| 1 KB × 1億 | ≒ 100 GB | 1億件の小さなレコード |
| 1 MB × 100万 | ≒ 1 TB | 100万枚の画像 |
2-8法則の見積もりへの応用
ほとんどのシステムは80/20法則に従います。20%のデータが80%のリクエストを処理します。つまり:
- キャッシュサイズ ≒ 総データ量 × 20%
- ホットスポットQPS ≒ 総QPS × 80%が20%のキーに集中
- キャッシュヒット率の目標 ≒ 80%以上(これより低いとキャッシュ戦略に問題がある)
3. コア設計パターン
システム設計で繰り返し現れるパターンです。これらを把握すれば、ほとんどのシナリオに対応できます。
3.1 キャッシュパターン
| パターン | 読み取りパス | 書き込みパス | 適用シナリオ |
|---|---|---|---|
| Cache-Aside | まずキャッシュを検索、ミスならDBを検索してキャッシュに書き戻す | まずDBに書き込み、その後キャッシュを削除 | 汎用シナリオ、最も一般的 |
| Read-Through | キャッシュ層が自動的にDBからロード | Cache-Asideと同じ | キャッシュフレームワークのサポートが必要 |
| Write-Behind | Cache-Asideと同じ | まずキャッシュに書き込み、非同期でDBに書き込む | 書き込み集中型、データ損失を許容できる場合 |
なぜ「キャッシュの更新」ではなく「キャッシュの削除」なのか?
キャッシュの更新は並行処理のシナリオでデータの不整合を引き起こしやすくなります。スレッドAとBが同時に更新する際、Aが先にDBに書き込んだのにBが先にキャッシュを更新してしまい、キャッシュにBの古い値が残る可能性があります。キャッシュの削除なら、次回の読み取りリクエストでDBから再ロードされるため、この問題を自然に回避できます。
3.2 シャーディング
単一テーブルのデータ量が千万件を超えるか、単一データベースのQPSがボトルネックに達した場合、シャーディングを検討する必要があります。
| 戦略 | やり方 | メリット | デメリット |
|---|---|---|---|
| 垂直分割 | ビジネスドメインごとにデータベースを分割 | ビジネスの疎結合、独立したスケーリング | クロスデータベースのJOINが困難 |
| 水平分割 | 同じテーブルをルールに従って複数のテーブルに分割 | 単一テーブルのデータ量をコントロール可能 | シャードキーの選択が重要 |
| 垂直テーブル分割 | 大きなフィールドを独立したテーブルに分割 | IOの削減、クエリ効率の向上 | 追加のJOINが必要 |
シャードキーの選択原則:
- 最も頻繁にクエリされるフィールドを選択する(例:user_id)
- データの分布を均等にし、ホットスポットを避ける
- 同一ユーザーのデータを同じシャードに配置する(クロスシャードクエリの削減)
3.3 メッセージキュー
メッセージキューは分散システムの「ショックアブソーバー」で、中核的な役割は疎結合、非同期化、ピークの平滑化です。
| シナリオ | キューを使わない場合 | キューを使う場合 |
|---|---|---|
| 注文後の通知送信 | 注文APIが同期的に通知サービスを呼び出し、通知の失敗が注文の失敗につながる | 注文成功後にメッセージを送信し、通知サービスが非同期で消費 |
| フラッシュセール | 瞬間的なトラフィックがデータベースを圧倒 | リクエストがまずキューに入り、バックエンドが処理能力に応じて消費 |
| データ同期 | サービスAがサービスBのAPIを直接呼び出す | サービスAがイベントを発行し、サービスBが購読して処理 |
4. トレードオフ思考:銀の弾丸はない
アーキテクチャ設計の本質はトレードオフです。すべての決定には代償があり、代償を理解した上で現在の段階に適した選択をすることが重要です。
| トレードオフの軸 | 選択肢A | 選択肢B | 判断基準 |
|---|---|---|---|
| 一貫性 vs 可用性 | 強一貫性(CP) | 高可用性(AP) | ビジネスが一時的な不整合を許容できるか? |
| パフォーマンス vs コスト | フルキャッシュ | オンデマンドキャッシュ | データ量と予算 |
| シンプルさ vs 柔軟性 | モノリスアーキテクチャ | マイクロサービス | チーム規模とビジネスの複雑さ |
| リアルタイム vs バッチ | ストリーム処理 | バッチ処理 | データの即時性の要件 |
| 自社構築 vs マネージド | 自前でMySQLを構築 | クラウドデータベース(RDS)を利用 | 運用能力とコスト |
アーキテクチャ決定記録(ADR)
重要なアーキテクチャの決定はすべて記録すべきです。背景は何か、どの選択肢を検討したか、なぜこれを選んだか、どのような代償があるか。これは責任転嫁のためではなく、後の人が「なぜ当時こう設計したのか」を理解できるようにするためです。
フォーマットはシンプルです:
- タイトル:XXXをYYYに置き換える
- 背景:どのような問題に直面したか
- 決定:どの案を選択したか
- 理由:なぜこれを選んだか
- 代償:この決定のデメリットとリスク
よくある誤ったトレードオフ
| 間違い | 表れ | 正しいアプローチ |
|---|---|---|
| 早すぎる最適化 | DAU1000でシャーディングを導入 | まず単一DBを使い、ボトルネックに遭遇してから分割 |
| 技術主導 | 「Kafkaを使いたい」ではなく「非同期が必要」が先 | 技術ではなく問題から出発する |
| 運用コストの無視 | 最適な案を選んだがチームが維持できない | ソリューションはチームの能力に合わせる |
| 完全な一貫性の追求 | すべてのシナリオで分散トランザクションを使用 | ほとんどのシナリオでは結果整合性で十分 |
5. 古典的な例
3つの古典的な例を通じて、これまでに学んだ方法論を統合します。
5.1 短縮URLサービス(TinyURL)
短縮URLサービスはシステム設計面接の古典的な問題で、小さいながらも要素が揃っています。
要件の明確化:
- コア機能:長いURL → 短いURL(書き込み)、短いURL → リダイレクト(読み取り)
- 読み書き比:約100:1(読み取りが書き込みより遥かに多い)
- 日平均リダイレクト:1億回
- 短縮URLは期限切れにならない
キャパシティ見積もり:
| 指標 | 計算 | 結果 |
|---|---|---|
| 書き込みQPS | 1億 / 100 / 86400 | ≒ 12 QPS |
| 読み取りQPS | 1億 / 86400 | ≒ 1,200 QPS |
| ピーク読み取りQPS | 1,200 × 3 | ≒ 3,600 QPS |
| 5年間のストレージ | 100万/日 × 365 × 5 × 100B | ≒ 18 GB |
| キャッシュ(20%) | 18 GB × 20% | ≒ 3.6 GB |
アーキテクチャ設計:
書き込みパス:クライアント → API Server → IDジェネレータ → Base62エンコード → MySQL + Redisに書き込み
読み取りパス:クライアント → CDN → API Server → Redis検索 → 302リダイレクト
↓ (cache miss)
MySQL検索 → Redisに書き戻し主要な設計上の決定:
- 短縮コードの生成:Snowflake分散ID + Base62エンコードでハッシュ衝突を回避
- キャッシュ戦略:Cache-Aside、ホットな短縮URLはCDNで高速化
- データベース:単一テーブルで十分(18GBは小さい)、短縮コードでインデックス
5.2 フィードシステム
ソーシャルプラットフォームのフィード(WeChatのモーメンツ、Weiboのホームタイムライン)はもう1つの古典的な問題です。
コアの課題:ユーザーが投稿を公開したとき、すべてのフォロワーにどうやって届けるか?
| 方式 | やり方 | メリット | デメリット |
|---|---|---|---|
| プルモデル(Pull) | 読み取り時にリアルタイムでフォローの投稿を集約 | 書き込みがシンプル、ストレージが少ない | 読み取りが遅い、フォローが多いとレイテンシが高い |
| プッシュモデル(Push) | 投稿時に全フォロワーの受信トレイに書き込む | 読み取りが極めて高速 | インフルエンサーの投稿で書き込み拡散が深刻 |
| プッシュ・プル併用 | 一般ユーザーはプッシュ、インフルエンサーはプル | 読み書きのパフォーマンスのバランス | 実装が複雑 |
プッシュ・プル併用の設計:
- フォロワー数 < 1万:投稿時に全フォロワーのフィードキャッシュにプッシュ(プッシュモデル)
- フォロワー数 > 1万:プッシュせず、フォロワーが読み取り時にリアルタイムで取得(プルモデル)
- ユーザーがフィードを開いた時:プッシュされた内容 + インフルエンサーのリアルタイム取得内容をマージし、時系列でソート
5.3 フラッシュセールシステム
フラッシュセールのコアの課題は、瞬間的な超高同時アクセスと在庫のオーバーセルを防ぐことです。
トラフィックの特徴:
- イベント開始前:大量のユーザーがページをリロードして待機
- イベント開始の瞬間:QPSが平常時の100倍以上になる可能性
- イベント終了後:トラフィックが急速に低下
多段階ピーク平滑化戦略:
ユーザーリクエスト → CDN(静的ページ)→ ゲートウェイ(レートリミット)→ メッセージキュー(ピーク平滑化)→ 在庫サービス(引き当て)| 層 | 戦略 | 効果 |
|---|---|---|
| フロントエンド | ボタン無効化 + ランダム遅延 + CAPTCHA | ボットの排除、リクエストの分散 |
| CDN | 静的リソースのキャッシュ | ページリクエストの90%を削減 |
| ゲートウェイ | トークンバケットによるレートリミット | システムが処理可能なトラフィックのみ通過 |
| メッセージキュー | リクエストをキューに入れ、非同期処理 | ピークの平滑化、データベースの保護 |
| 在庫サービス | Redisによる事前引き当て + Luaのアトミック操作 | オーバーセルの防止、ミリ秒級の応答 |
フラッシュセールの核心原則
- 上流でできるだけブロックする:CDNで止められるものはアプリケーション層に到達させない
- 読み書きの分離:商品詳細ページはキャッシュ、注文だけがデータベースにアクセス
- 非同期処理:ユーザーが「購入」をクリックしたら直ちに「処理中」を返し、バックグラウンドで非同期処理
- フォールバック:レートリミット、サーキットブレーカー、グレードダウン、どの層に問題が発生してもPlan Bを用意
まとめ
システム設計は実践的なスキルであり、構造化された思考とトレードオフの取捨選択が鍵となります。
本章の主要なポイントを振り返りましょう:
- 4ステップフレームワーク:要件の明確化 → キャパシティ見積もり → アーキテクチャ設計 → 深掘り最適化。各ステップはスキップできない
- 封筒の裏の計算:正確である必要はなく、オーダーが分かればよく、アーキテクチャの決定を導く
- コアパターン:キャッシュ、シャーディング、メッセージキュー、CDN、レートリミット・サーキットブレーカーはシステム設計の「積み木」
- トレードオフ思考:完璧なソリューションはなく、現在の段階に適したソリューションがあるだけ。すべての決定の理由と代償を記録する
- 古典的な例:短縮URLサービスで基礎を練習し、フィードでプッシュ・プルモデルを練習し、フラッシュセールで高同時アクセスを練習する。この3つをマスターすれば応用が利く
関連資料
- System Design Interview - Alex Xuのシステム設計面接の古典
- Designing Data-Intensive Applications - Martin Kleppmannのデータ集約型アプリケーション設計
- The System Design Primer - GitHubで最も充実したシステム設計学習リソース
- ByteByteGo - Alex Xuのシステム設計ビジュアルブログ