キャパシティ見積もり(写真共有を例に)
アーキテクチャの選択は好みではなく数字から導く。1000 万 DAU の写真共有サービスを例に、写真 1 枚が何バイトかから出発して、1 日のストレージ、年間の総量、読み書きの QPS、配信帯域、キャッシュサイズまでを順に見積もる。出てきた数字が「写真は DB でなくオブジェクトストレージ」「配信は CDN 必須」「メタデータはシャーディング」という選択を 1 つずつ強制していく過程を追う。
この章で読むもの
ここまでの分散システム編は部品(Raft、レプリケーション、コンシステントハッシュ、分散トランザクション)を 1 つずつ作ってきた。この章は逆向きに、要件から出発して部品を選ぶ練習をする。題材は定番の写真共有サービス(Instagram 型)で、道具は掛け算と割り算だけだ。
見積もりの目的は正確な予測ではない。桁を掴むことだ。44TB/日 と 44GB/日 では選ぶ技術がまるで違う。桁さえ合っていれば、「この構成では無理」「この構成なら余裕」という判断には十分で、実務でも新システムの設計はこの計算から始まる。
① 前提を数字に置く
計算を始める前に、要件を数字に固定する。ここを曖昧にしたまま進むと、後の計算がすべて宙に浮く:
ユーザー DAU 1000万人
書き込み 1人あたり 2枚/日 投稿
読み取り 1人あたり 100枚/日 閲覧
写真 原本 平均2MB / 配信用(リサイズ済み) 200KB
メタデータ 1枚あたり 1KB(投稿者、時刻、キャプション、いいね数)
係数 ピークは平均の3倍 / レプリカ3本 / 保存期間は無期限「写真は何バイトか」が全計算の起点になることに注意したい。スマホ撮影の JPEG は 2〜5MB、リサイズ後のタイムライン表示用は 100〜300KB。この 1 桁の差(原本 2MB と配信用 200KB)が、後で「保存の設計」と「配信の設計」を分離する理由になる。
② ストレージ: 44TB/日 という現実
書き込まれる量を積み上げる:
投稿数 1000万人 × 2枚 = 2000万枚/日
保存量/日 2000万枚 × (2MB + 200KB) = 44 TB/日
保存量/年 44 TB × 365 ≈ 16 PB/年
実容量/年 16 PB × レプリカ3本 = 48 PB/年年 48PB。この数字が最初の選択を強制する。写真のバイナリをリレーショナル DB の BLOB に入れる案は、この時点で消える。B-Tree 系のストレージはページ単位の更新と索引に最適化されていて、追記されるだけの数 MB のバイナリを何 PB も抱える用途には合わない。バックアップもレプリケーションも劣化する。
選ぶのはオブジェクトストレージ(S3 系)だ。フラットな空間に不変オブジェクトを置く前提の設計で、容量は水平に伸び、コンシステントハッシュで見た分散の仕組みがそのまま中で動いている。DB に残すのはメタデータ 1KB と、オブジェクトストレージへのキーだけになる。
③ QPS: 読みと書きは別の問題
次に毎秒の負荷を出す。1 日は 86,400 秒、暗算用には約 10 万秒と置く:
書き込み 2000万枚/日 ÷ 86,400秒 ≈ 230 QPS(ピーク 700)
読み取り 1000万人 × 100枚 = 10億閲覧/日 ≈ 11,600 QPS(ピーク 35,000)読み書き比はおよそ 50:1。この非対称が構成を決める。書き込み 700 QPS は単一のキューやアプリサーバ数台で十分さばける量だが、読み 35,000 QPS をオリジンサーバで受けるのは無謀で、読み側だけを別の仕組みで受ける必要がある。レプリケーションで見た「リーダーに書き、フォロワーで読む」非対称構成の、さらに徹底した形が要る。
④ 帯域: CDN が必須になる瞬間
読み QPS を帯域に換算すると、その「別の仕組み」が何かはっきりする:
配信帯域 35,000 QPS × 200KB = 7 GB/s ≈ 56 Gbps56Gbps を自前のデータセンターから出し続けるのは、回線費用でもレイテンシでも成立しない。写真は不変(更新されない)なので、キャッシュ可能性は理想的だ。よって配信は CDN に寄せ、オリジンへの到達は CDN ミス分だけにする。ヒット率 90% なら、オリジンの負荷は 3,500 QPS・5.6Gbps まで落ちて、現実的な台数に収まる。
「原本 2MB は保存用、配信は 200KB」という最初の分離もここで効く。もし原本をそのまま配っていたら帯域は 10 倍の 560Gbps で、CDN 費用も 10 倍になる。アップロード時に非同期でリサイズ版を作る(メッセージキュー の出番)のは、この帯域計算の帰結だ。
⑤ キャッシュとメタデータ: 残りの部品を数字で選ぶ
閲覧には 80/20 則を仮定する。アクセスの 8 割は直近の人気写真 2 割に集中する:
ホット対象 直近3日の投稿 6000万枚 × 上位20% = 1200万枚
キャッシュ 1200万枚 × 200KB = 2.4 TB2.4TB はメモリ 128GB のキャッシュサーバ 20 台弱。LRUキャッシュで作った追い出しがそのまま動く規模で、CDN の内側にこの層を置けばオリジンはさらに軽くなる。
メタデータ側も数字にする。2000 万行/日 × 1KB = 20GB/日、年 7TB。行数は年 73 億に達するので、単一 DB の索引が苦しくなる。photo_id を ID生成の Snowflake 形式(時刻順)にし、ユーザー単位でシャーディングして、読みはレプリカへ逃がす。「いいね数」のような高頻度更新は行本体から分離してカウンタ専用の仕組みに置く、という分岐もこの行数から出てくる。
動かす
下のデモはこの見積もりを 1 段ずつ進める電卓で、DAU を 100 万 / 1000 万 / 1 億に切り替えると全段の数字が連動して再計算される。どの規模で「単一 DB で足りる」が「シャーディング必須」に変わるか、帯域が何 Gbps になったら CDN 無しが不可能になるか、境界を自分で確かめられる。
すべての計算の起点。ここから保存・QPS・帯域が芋づる式に決まる
前提: 1人2枚/日投稿・100枚/日閲覧・原本2MB/配信200KB/メタデータ1KB・ピーク3倍・レプリカ3本。 DAU を切り替えると全段が再計算され、「単一DBで持つ/シャーディング必須」「CDN があった方が安い/無いと不可能」の 境界がどの規模で跨がれるかが分かる。
設計の観点
- 見積もりの流儀: 1 日 ≈ 10 万秒、1 年 ≈ 400 日のような丸めで暗算する。目的は桁で、有効数字 1 桁で十分。ピーク係数(2〜5 倍)と成長率(年 2 倍なら 3 年で 8 倍)を最後に掛ける
- 数字が選択を強制する順番: バイト数 → ストレージ方式 → QPS → 読み書き分離 → 帯域 → CDN → ホット率 → キャッシュ層。逆に言えば、この順で聞かれた数字に答えられない設計は根拠がない
- 読み書き比が構成を決める: 50:1 なら読み側の複製とキャッシュに投資する。逆に書きが支配的(ログ収集、IoT)なら WAL や メッセージキュー 系の追記構成に寄せる。同じ「分散システム」でも部品の選び方が逆になる
- 不変データはキャッシュの味方: 写真は書き換わらないので TTL も無効化も要らない。可変データ(いいね数)を分離するのは、キャッシュ可能性を汚染しないため
- 見積もりの賞味期限: DAU が 10 倍になれば境界を跨ぐ選択が出る(単一 DB → シャード、単一リージョン → マルチリージョン)。設計書には「この構成は何倍まで持つか」を書き添える
選択肢の比較と実例
| 写真の置き場所 | 強み | 弱み | 実例 |
|---|---|---|---|
| RDB の BLOB | トランザクションで一貫 | PB 級で破綻、バックアップ劣化 | 小規模の社内システム止まり |
| ファイルシステム + NFS | 単純 | メタデータがボトルネック、水平拡張が困難 | 初期の小規模サービス |
| オブジェクトストレージ | 水平拡張、不変前提、安い | 一覧・検索は別途メタデータ DB が要る | Instagram(S3)、ほぼ全ての現行サービス |
| 専用ブロブストア | 小ファイル大量に最適化 | 自前運用の重さ | Facebook Haystack(メタデータをメモリに置き I/O を 1 回にした) |
裏どり:
- Instagram 初期(2011): 3 人のエンジニアが S3 + PostgreSQL + memcached で数千万ユーザーをさばいた構成が公開されている。写真はオブジェクトストレージ、メタデータは RDB という分離の古典例
- Facebook Haystack(2010): 小さい写真を大量に配るとファイルシステムのメタデータ I/O が支配的になる、という測定から専用ストアを自作した論文。「数字が設計を強制する」の実例そのもの
- Jeff Dean の Numbers Everyone Should Know: メモリ参照 100ns、データセンター内往復 500μs、ディスクシーク 10ms。見積もりの単価表として広く使われる
- DDIA(Kleppmann)1 章: 負荷の記述(QPS、読み書き比、データ量)を設計の第一歩に置く整理。この章の進め方はその実践
簡略化したこと
- 単一リージョン前提: マルチリージョン(地理的レプリケーション、リージョン間の一貫性)は扱わない
- フィード生成は扱わない: フォロー関係のタイムライン(push/pull、ファンアウト)はそれ自体が 1 章分の題材
- 費用計算なし: ストレージ単価 × PB で費用も見積もれるが、価格は変動が速いので式だけに留めた
- 動画なし: 動画は 1 桁上のバイト数とトランスコードのパイプラインが加わる
参考資料
- Scaling Instagram — 初期 Instagram の構成
- Beaver et al., Finding a needle in Haystack(2010) — Facebook の写真ストア
- Martin Kleppmann, Designing Data-Intensive Applications 1 章 — 負荷の記述と見積もり
- Latency Numbers Every Programmer Should Know — 見積もりの単価表