見積もりの技術 — バックオブエンベロープ計算
「数字は自信を生む」
面接の前日、ソウタはカフェでノートを広げていた。
Webエンジニア歴4年。Railsでのバックエンド開発ならそこそこ自信がある。でも「10万DAUのシステムを設計してください」という問いに、自分が即答できる気がしなかった。DAUが10万だとして、それが何を意味するのか——QPSに換算するといくらになるのか、ストレージは何TBか——頭の中でまったく像を結ばない。
数字が出てこないと、設計の話に自信を持って踏み込めない。面接官の前で「えーっと……」と詰まる自分が見えた。
翌日のセッション、レイカはいつものようにコーヒーを飲んでいた。ソウタが「計算が苦手で……」と切り出すと、彼女は少し笑った。
「安心して。ここで言う計算は小学生レベル。掛け算と割り算しか使わない。でも、これができるかできないかで面接官の目が変わる。」
レイカはホワイトボードにこう書いた。
「設計のアイデアを持っている人」は多い。
「数字で設計を語れる人」は少ない。
「元AWSのソリューションアーキテクトとして、何百人もの候補者の面接をしてきた。数字のない設計提案は、地図のない旅行計画と同じ。どこへ向かっているかわからない。」
「でも、正確な数字を出すのは難しくないですか?」
「正確さは要らない。オーダー(桁)が合っていれば十分。1万と10万は違う。でも8,642と10,000は同じ。それがバックオブエンベロープ計算の本質。」
ソウタは少し気が楽になった。桁が合えばいい。それならできるかもしれない。
覚えるべき基本値
「まず頭に入れる数字がある。これは完全に暗記していい。」
時間の単位
# 時間換算(覚えておくべき数字)
1分 = 60秒
1時間 = 3,600秒 ≒ 4,000秒(丸めてOK)
1日 = 86,400秒 ≒ 10万秒(最重要!)
1週間 = 約60万秒
1ヶ月 = 約260万秒
1年 = 約3,000万秒(3 × 10^7)「1日が約10万秒というのが一番よく使う。QPS計算の基本だ。」
データサイズの単位
# データ量の単位
1 KB = 1,024 バイト ≒ 10^3 バイト
1 MB = 1,024 KB ≒ 10^6 バイト
1 GB = 1,024 MB ≒ 10^9 バイト
1 TB = 1,024 GB ≒ 10^12 バイト
1 PB = 1,024 TB ≒ 10^15 バイト
# データ型別サイズ(概算)
文字1文字 = 1〜2バイト(ASCII/UTF-8)
整数(int) = 4バイト
Long型 = 8バイト
UUID/GUID = 36バイト(文字列)/ 16バイト(バイナリ)
日時(timestamp) = 8バイト
ユーザーレコード = 約1KB
Twitterツイート = 約300バイト
画像(サムネイル) = 約300KB
フルサイズ画像 = 約3MB
動画(1分/1080p) = 約150MBレイテンシの数字(重要)
「これも覚えておく。面接でキャッシュが有効かどうか判断するときに使う。」
| 操作 | レイテンシ |
|---|---|
| L1キャッシュ参照 | 0.5 ns |
| L2キャッシュ参照 | 7 ns |
| メモリ参照(RAM) | 100 ns |
| SSD ランダム読み取り | 150 µs |
| HDD ランダム読み取り | 10 ms |
| 同じリージョン内 RTT | 0.5 ms |
| Redis 読み取り | < 1 ms |
| RDS クエリ(シンプル) | 1〜10 ms |
| 別リージョン間 RTT | 150 ms |
INFO
「メモリはSSDより1000倍速い、SSDはHDDより100倍速い」という大まかな感覚を持つだけでよい。具体的な数字が活きるのは「なぜキャッシュが必要か」を説明するときや、「リアルタイム性の要件にHDDが使えない理由」を語るとき。面接での決め台詞: 「DBの応答は10ms、Redisは1ms以下なので、頻繁にアクセスするデータはElastiCacheに乗せます」。
可用性の数字
# SLA(Service Level Agreement)
可用性99% → 年間ダウン時間 約88時間
可用性99.9% → 年間ダウン時間 約9時間 ("スリーナイン")
可用性99.99% → 年間ダウン時間 約1時間 ("フォーナイン")
可用性99.999%→ 年間ダウン時間 約5分 ("ファイブナイン")INFO
「フォーナイン(99.99%)」は多くのエンタープライズシステムの標準目標。これ以上を目指すと費用が指数関数的に増加する。面接では最初の要件確認で「何ナイン必要ですか」と聞き、その答えに応じてアーキテクチャの冗長性を設計すること。99.9%ならシングルAZでも許容できる場合がある。99.99%ならマルチAZ必須。
QPS(Query Per Second)の計算
「最もよく使う計算がQPS。1秒あたりのリクエスト数だ。」
基本の公式
QPS = 1日のリクエスト数 / 1日の秒数(86,400)
計算の簡略化:
1日のリクエスト数 ÷ 10万 ≒ QPS
練習問題1: Instagram
前提:
- DAU: 5億(500M)
- 1人あたり1日の操作: 投稿2回、閲覧100回
投稿QPS(書き込み):
500M × 2 / 86,400 ≒ 500M × 2 / 100,000
= 1,000M / 100,000
= 10,000 posts/sec
閲覧QPS(読み込み):
500M × 100 / 86,400 ≒ 500,000 req/sec
ピーク時(2〜3倍を想定):
書き込みピーク: 20,000〜30,000 posts/sec
読み込みピーク: 1,000,000〜1,500,000 req/sec
INFO
ピーク係数は一般的に2〜3倍を使う。ただし「地震速報」「人気アーティストのライブ告知」「大みそかの年越しカウントダウン」など特定イベントのスパイクは10〜20倍になることも。システムの性質によって係数を変える。Instagramのようなソーシャル系は夜8〜10時にピークが来ることが多い。
練習問題2: Slack
前提:
- DAU: 1,000万(10M)
- 1人あたり1日のメッセージ: 50件
- 1メッセージ: 約500バイト
メッセージQPS:
10M × 50 / 86,400 ≒ 500M / 100,000 = 5,000 messages/sec
ストレージ/日:
10M × 50 × 500B = 250GB/day
5年間のストレージ:
250GB × 365 × 5 ≒ 456TB ≒ 約0.5PB
練習問題3: TikTok
「TikTokでやってみましょうか」とソウタが言った。「最近よく聞く題材だし……」
「いいね。じゃあ自分で仮定を置いてみて。」
前提(ソウタの仮定):
- DAU: 10億(1B)
- 1人あたり1日の視聴動画数: 30本
- 1人あたり1日の投稿: 0.1本(10人に1人が投稿)
- 1本の動画(60秒/720p): 約100MB
投稿QPS(書き込み):
1B × 0.1 / 86,400 ≒ 100M / 100,000 = 1,000 uploads/sec
再生QPS(読み込み):
1B × 30 / 86,400 ≒ 300,000 plays/sec
1日のストレージ増加量(元データ):
1B × 0.1 × 100MB = 10PB/day
「前提と計算は正しい。ただ10PB/dayは元データの話。実際は複数解像度にエンコードするから3〜5倍になる。それと古い動画はCold Storage(Amazon S3 Glacier)に移して費用を下げる。このような補足ができると面接官の印象がさらに上がる。」
練習問題4: Uber Eats
前提:
- DAU: 5,000万(50M)
- 注文率: DAUの10% → 5M orders/day
- 注文1件あたりのAPIリクエスト: 20回
- ドライバー: 50万人、位置情報更新: 5秒に1回
注文関連QPS:
5M × 20 / 86,400 ≒ 100M / 100,000 = 1,000 req/sec
ドライバー位置情報QPS:
500,000 / 5 = 100,000 updates/sec
→ 位置情報の更新量が桁違いに多い
→ このシステムの設計難点は「位置情報の高頻度書き込み」
「この洞察が大事。QPSを計算したら、どこがボトルネックかを言えるようにする。Uber Eatsなら位置情報の書き込みが支配的。それがわかると、通常のRDBMSではなくRedisやAmazon DynamoDBで位置情報を管理する設計が見えてくる。」
練習問題5: Zoom
前提:
- 同時接続ミーティング: 100万(1M meetings)
- 1ミーティング参加者: 平均5人
- 映像ストリーム: 720p / 2Mbps/人
- 音声: 64Kbps/人
同時接続ユーザー: 1M × 5 = 5M users
帯域(映像):
5M × 2Mbps = 10,000,000 Mbps = 10Pbps
帯域(音声):
5M × 64Kbps = 320,000,000 Kbps = 320Tbps
WARNING
Zoomで見落としやすいのが「受信の非対称性」。5人のミーティングでは1人が他の4人分のストリームを受信する。サーバー中継モデル(SFU: Selective Forwarding Unit)の場合、サーバーが各参加者に全員分を転送するため、帯域は「人数 × ストリーム数」で膨らむ。P2Pモデルと大きく異なる点を面接で指摘できると差がつく。
ストレージの計算
練習問題6: YouTube
前提:
- 1日のアップロード動画: 500,000本
- 1本の動画(元データ): 1GB
- エンコード後の形式: 3種(360p / 720p / 1080p)
- 360p: 300MB
- 720p: 600MB
- 1080p: 1GB
- 保持期間: 10年
1日のストレージ増加量:
元データ: 500,000 × 1GB = 500TB
エンコード:
360p: 500,000 × 300MB = 150TB
720p: 500,000 × 600MB = 300TB
1080p: 500,000 × 1GB = 500TB
合計: 500 + 150 + 300 + 500 = 1,450TB ≒ 1.5PB/day
10年間の総ストレージ:
1.5PB × 365 × 10 ≒ 5,475PB ≒ 5.5EB(エクサバイト)
「この規模になると、単一のデータセンターに収まらない。だからGoogleは世界中のデータセンターで分散保存している。AWSでやるなら、S3を複数リージョンにCross-Region Replicationする設計になる。」
帯域幅の計算
「ストレージの次は帯域。1秒あたりのデータ転送量を考える。」
計算の基本
帯域(bps) = QPS × 平均レスポンスサイズ(バイト)× 8
※ バイト → ビット変換に × 8 が必要
練習問題7: Netflix
前提:
- 同時視聴ユーザー: 3,000万(30M)
- 1080p 動画: 5Mbps(メガビット/秒)
- 4K 動画: 25Mbps
- ユーザーの70%が1080p、30%が4K
必要な帯域幅:
1080p: 30M × 0.7 × 5Mbps = 105,000,000 Mbps = 105Tbps
4K: 30M × 0.3 × 25Mbps = 225,000,000 Mbps = 225Tbps
合計: 330Tbps
→ Netflixは実際にこの規模のCDNを世界中で運用している
WARNING
帯域はビット(b)とバイト(B)を混同しやすい。1Mbps(メガビット/秒)= 0.125MB/s(メガバイト/秒)。ネットワーク帯域はビット、ストレージ容量はバイトで表すのが一般的。計算後に「この帯域はbpsで表現しています」と一言添えるだけで信頼感が上がる。単位を間違えると桁が8倍ずれる。
AWSでのキャパシティ計画
「数字を計算したら、次は『何台のサーバーが必要か』に落とし込む。これがキャパシティ計画だ。」
AWSの主なサービスとスループット目安
# EC2 インスタンス別スループット(Railsアプリ)
c5.large:
CPU: 2 vCPU
メモリ: 4GB
ネットワーク: 最大 10Gbps
Railsアプリ目安: 500〜1,000 req/sec
c5.4xlarge:
CPU: 16 vCPU
メモリ: 32GB
ネットワーク: 最大 10Gbps
Railsアプリ目安: 4,000〜8,000 req/sec
c5.18xlarge:
CPU: 72 vCPU
メモリ: 144GB
ネットワーク: 最大 25Gbps
Railsアプリ目安: 20,000〜40,000 req/sec
# RDS PostgreSQL(db.r6g.large)
RDS_r6g_large:
接続数: 最大 1,000
読み込みQPS: 約 10,000
書き込みQPS: 約 5,000
ストレージ: 最大 64TB
# ElastiCache Redis(cache.r6g.large)
ElastiCache_r6g_large:
操作: 100,000+ ops/sec
レイテンシ: < 1ms
メモリ: 13.07GB
# CloudFront(CDN)
CloudFront:
エッジロケーション: 400+
スループット: 数百Tbps
レイテンシ低減: オリジンの1/10以下必要サーバー台数の計算
「InstagramレベルのQPS(読み込み50万req/sec)をAWSで処理するには何台必要か計算してみよう。」
目標QPS: 500,000 req/sec
1台あたり処理能力:
- c5.4xlarge の Rails アプリ: 5,000 req/sec(安全マージン込み)
必要台数(通常時):
500,000 / 5,000 = 100台
ピーク時(3倍):
500,000 × 3 / 5,000 = 300台
→ Auto Scaling で 100〜300 台の間で伸縮する構成
RDS 設計:
- 書き込み: 10,000 req/sec
→ RDS Writer 1台(db.r6g.2xlarge)で対応可能
- 読み込み: 490,000 req/sec
→ ElastiCache でキャッシュヒット率90%を目標にすると
DBに到達するのは49,000 req/sec
→ Read Replica 5台で対応可能
RailsアプリでのQPS計測例
「実際のRailsアプリでどう計測・対処するか、コードで見せよう。」
現状のQPSをRedisで計測する
# config/initializers/request_counter.rb
# Redis を使って1分間のリクエスト数をカウントする
class RequestCounter
REDIS = Redis.new(url: ENV.fetch("REDIS_URL"))
def self.increment
key = "qps:#{Time.current.strftime('%Y%m%d%H%M')}"
REDIS.incr(key)
REDIS.expire(key, 120) # 2分後に期限切れ
end
def self.current_qps
key = "qps:#{1.minute.ago.strftime('%Y%m%d%H%M')}"
count = REDIS.get(key).to_i
count / 60.0 # 1分間のカウントを秒換算
end
end
# application_controller.rb に追記
before_action { RequestCounter.increment }キャッシュによるDB負荷軽減
# app/models/article.rb
class Article < ApplicationRecord
# ランキングは ElastiCache (Redis) にキャッシュ
# TTL: 5分(300秒)
def self.popular_cached
Rails.cache.fetch("articles:popular", expires_in: 5.minutes) do
# このブロックはキャッシュミス時だけ実行される(DBアクセス)
order(view_count: :desc).limit(20).to_a
end
end
# キャッシュヒット率をログに記録してモニタリング
def self.popular_with_metrics
hit = true
result = Rails.cache.fetch("articles:popular", expires_in: 5.minutes) do
hit = false
order(view_count: :desc).limit(20).to_a
end
Rails.logger.info(
event: "cache_access",
key: "articles:popular",
hit: hit
)
result
end
endストレージ使用量を概算するRakeタスク
# lib/tasks/capacity.rake
namespace :capacity do
desc "現在のストレージ使用量と増加ペースを概算する"
task estimate: :environment do
week_ago = 7.days.ago
posts_week = Post.where(created_at: week_ago..)
daily_posts = posts_week.count / 7.0
avg_size_bytes = 500 # 1投稿の平均サイズ(バイト)
daily_storage_gb = (daily_posts * avg_size_bytes) / 1.0e9
yearly_storage_tb = daily_storage_gb * 365 / 1000
puts "=== ストレージ概算レポート ==="
puts "直近7日間の日次投稿数: #{daily_posts.round} posts/day"
puts "1日のストレージ増加: #{daily_storage_gb.round(2)} GB/day"
puts "1年間の見込み増加量: #{yearly_storage_tb.round(2)} TB/year"
puts "5年間の見込み増加量: #{(yearly_storage_tb * 5).round(2)} TB"
end
endGolangでの概算ツール実装例
「GoでCLIの概算ツールを作るとするとこうなる。面接の準備や設計レビューで使える。」
// capacity/estimator.go
package capacity
import "fmt"
// Estimate はシステムの概算値を計算して出力する
type Estimate struct {
DAU int64 // 日次アクティブユーザー数
ActionsPerUser float64 // 1ユーザーあたりの日次アクション数
DataSizeBytes int64 // 1アクションあたりのデータサイズ(バイト)
ReadWriteRatio float64 // 読み書き比(読み込みが何倍多いか)
PeakMultiplier float64 // ピーク係数(通常の何倍か)
RetentionYears int // データ保持年数
}
func (e *Estimate) Calculate() {
const secondsPerDay = 86_400
// QPS 計算
writeQPS := float64(e.DAU) * e.ActionsPerUser / secondsPerDay
readQPS := writeQPS * e.ReadWriteRatio
peakWriteQPS := writeQPS * e.PeakMultiplier
peakReadQPS := readQPS * e.PeakMultiplier
// ストレージ計算
dailyWriteCount := float64(e.DAU) * e.ActionsPerUser
dailyStorageGB := dailyWriteCount * float64(e.DataSizeBytes) / 1e9
totalStorageTB := dailyStorageGB * 365 * float64(e.RetentionYears) / 1000
// 帯域計算(レスポンスサイズはリクエストの5倍と仮定)
bandwidthMbps := readQPS * float64(e.DataSizeBytes) * 5 * 8 / 1e6
fmt.Println("=== システム概算 ===")
fmt.Printf("書き込みQPS(通常): %.0f req/sec\n", writeQPS)
fmt.Printf("読み込みQPS(通常): %.0f req/sec\n", readQPS)
fmt.Printf("書き込みQPS(ピーク): %.0f req/sec\n", peakWriteQPS)
fmt.Printf("読み込みQPS(ピーク): %.0f req/sec\n", peakReadQPS)
fmt.Printf("1日のストレージ増加: %.2f GB/day\n", dailyStorageGB)
fmt.Printf("%d年後の総ストレージ: %.2f TB\n", e.RetentionYears, totalStorageTB)
fmt.Printf("必要帯域幅: %.0f Mbps\n", bandwidthMbps)
}// main.go での使用例
package main
import "myapp/capacity"
func main() {
e := &capacity.Estimate{
DAU: 10_000_000, // 1000万 DAU(Slackのイメージ)
ActionsPerUser: 50, // 1日50メッセージ
DataSizeBytes: 500, // 1メッセージ500バイト
ReadWriteRatio: 10, // 読み書き比 10:1
PeakMultiplier: 3, // ピーク3倍
RetentionYears: 5, // 5年保持
}
e.Calculate()
// 出力例:
// 書き込みQPS(通常): 5787 req/sec
// 読み込みQPS(通常): 57870 req/sec
// 書き込みQPS(ピーク): 17361 req/sec
// 読み込みQPS(ピーク): 173611 req/sec
// 1日のストレージ増加: 250.00 GB/day
// 5年後の総ストレージ: 456.25 TB
// 必要帯域幅: 11574 Mbps
}計算ミスしやすいポイントと対策
「面接中によくある計算ミスをまとめた。事前に知っておくだけで防げる。」
WARNING
よくある計算ミス TOP 5
- ビットとバイトの混同 — 帯域はbit (b)、ストレージはByte (B)。変換を忘れると8倍ずれる
- 1日の秒数の間違い — 86,400を8,640や100,000と間違えやすい。「10万秒」と丸めて概算し、誤差が気になるときだけ補正する
- ピーク係数の忘れ — 平均QPSでサーバー台数を決めるとピーク時に障害になる
- 読み書き比の見落とし — 「書き込みQPS × 読み書き比」で読み込みQPSを算出する。Twitterは読み書き比100:1以上
- 単位の累乗ミス — 1TBは10^12バイト。概算では1000GBと覚えておけば十分
計算テンプレート(面接直前チェックリスト)
# 面接で使う概算テンプレート(Rubyコメント形式)
# == ステップ1: 前提の確認 ==
# DAU: X M(要件から引き出す)
# 1人あたり日次アクション数: N 回
# 読み書き比: R : 1
# データサイズ/件: Y bytes
# ピーク係数: P 倍(通常2〜3)
# 保持期間: T 年
# == ステップ2: QPS ==
# 書き込みQPS = DAU × N / 86_400
# 読み込みQPS = 書き込みQPS × R
# ピークQPS = 読み込みQPS × P
# == ステップ3: ストレージ ==
# 1日の増加量 = DAU × N × Y bytes
# T年後の総量 = 1日の増加量 × 365 × T
# == ステップ4: 帯域 ==
# 帯域(bps) = 読み込みQPS × 平均レスポンスサイズ × 8
# == ステップ5: サーバー台数 ==
# 必要台数(通常)= 通常QPS / 1台あたりのQPS
# ピーク台数 = ピークQPS / 1台あたりのQPS
# Auto Scaling 範囲 = 通常台数 〜 ピーク台数ソウタの練習: Uber
「じゃあ練習してみよう。Uberを設計する場合のQPS概算をやってみて。」
ソウタは深呼吸した。以前なら「えーっと……」と詰まっていたが、今回は違う。ステップを頭の中でトレースしながら、ホワイトボードに書き始めた。
「DAUを3,000万とします。Uberには乗客とドライバーがいて、ドライバーは全体の20%と仮定します。」
ドライバー数: 3,000万 × 20% = 600万
位置情報QPS(ドライバーが4秒に1回送信):
6,000,000 / 4 = 1,500,000 = 1.5M writes/sec
乗客の位置確認QPS(乗客も4秒に1回確認):
2,400万(乗客)/ 4 = 6,000,000 = 6M reads/sec
ライドリクエストQPS:
- DAU 3,000万のうち30%が1日に平均2回リクエスト
- 3,000万 × 0.3 × 2 / 86,400 ≒ 208 req/sec ≒ 約200 req/sec
設計のポイント:
- 位置情報の書き込みが 1.5M req/sec と支配的
- RDBMSでは到底捌けない → Redis や DynamoDB が候補
「良くなってる」とレイカが言った。「前提を仮定して、声に出しながら計算できた。しかも読み書きを分けて、ボトルネックを特定した。これが正しいプロセス。数字は多少ずれてもいい。考え方が正しければ面接官は評価する。」
INFO
面接では「正確な数字」より「計算のプロセス」が評価される。仮定を明示して、その仮定に基づいて計算する姿勢が重要。「1日のアクティブ率を30%と仮定して……」と声に出しながら進めること。黙って計算して結果だけ出すのは最もNGなパターン。面接官はプロセスを見ている。
面接での計算の見せ方テクニック
「数字が出せるようになったら、次は『どう見せるか』を磨く。」
実況中継スタイルで計算する
悪い例:
(しばらく沈黙)「……QPSは5,000です」
良い例:
「DAUを1,000万と仮定します。
1人が1日50件投稿するとすると、書き込みは1日5億件。
1日は約10万秒なので、5億 ÷ 10万 = 5,000 writes/sec です。
読み書き比を10:1とすると読み込みは50,000 req/sec、
ピーク3倍で150,000 req/sec が上限の目安になります。」
計算後に設計への橋渡しをする
「書き込みQPS 5,000 に対して、
RDS(PostgreSQL)の書き込み上限は約5,000 QPS。
ギリギリなので、書き込みをSQSキューに入れて
非同期処理する設計を検討します。
これでピーク時のDBへの直撃を防げます。」
単位を毎回確認して声に出す
「この数字はGB/dayです。」
「ここでTBに変換します。1TB = 1,000GBなので……」
「最終的な帯域はMbpsで表現します。」
まとめ: 概算の三原則
原則1: 丸める
86,400秒 → 10万秒(誤差17%は許容範囲)
1,024MB → 1,000MB(誤差2.4%は無視してよい)
原則2: 仮定を明示する
「ピーク係数を3倍と仮定すると……」
「読み書き比を10:1と仮定すると……」
「DAUの20%がドライバーと仮定すると……」
原則3: 計算しながら話す
頭の中で計算して結果だけ出すのはNG
プロセスを声に出して面接官に見せる
途中で間違えても「仮定が正しければ」という前提で評価される
次の章から、実際のシステム設計問題に取り組む。最初は「URL短縮サービス」——面接でよく出る定番問題だ。
「URL短縮は、サーバー設計の基礎が全部詰まった良問です」とレイカは言った。「シンプルに見えて、考えるべきことが山ほどある。今日練習したQPS計算が、さっそく使える。」
ソウタはノートの数字を見直した。桁が正しい。プロセスが正しい。数字への恐怖が、少しずつ設計への自信に変わっていくのを感じた。