「CPUはまだ80%」は大丈夫なのか
容量の見積もりに要る式は4つで、どれも掛け算と割り算で済む。ピーク時に毎秒3,000件が届くAPIを例に、台数・余力・接続数を順に決めていく。
平均だけでは、容量は決まらない
平均のQPSに平均のレイテンシを掛けて「足りている」と判断するのは、よくある失敗だ。障害が起きるのは平均の時間帯ではない。ピークの数分間、遅いほうの1%、AZが1つ落ちた瞬間に起きる。
この記事では、ひとつのAPIについて、同時処理数、台数、余力、接続数の順に見積もる。
リトルの法則は、3通りに使える
安定したシステムでは、中にある件数の平均Lは、到着率λと1件の滞在時間Wの積になる(L = λ × W)。中身がキューでもサーバーでも成り立つので、どの段にも当てはめられる。
| 知りたいこと | 式 | 例 |
|---|---|---|
| 必要な同時処理数 | L = λ × W | 毎秒500件 × 0.1秒 = 50件 |
| 上限から決まる到着率 | λ = L ÷ W | プール100 ÷ 0.2秒 = 毎秒500件 |
| 列の長さから見た待ち時間 | W = L ÷ λ | 列500件 ÷ 毎秒1,000件 = 0.5秒 |
2行目が特に役に立つ。プール100で応答100msなら毎秒1,000件まで受けられるが、DBが遅くなって200msになると、トラフィックは同じなのに受けられる量が半分になる。プールが詰まる障害は、この式で説明できる。
利用率と待ち時間は、曲線の関係にある
リクエストの到着にはばらつきがある。利用率が上がるほど、前の処理がまだ終わっていない瞬間が増え、待ちが生まれる。いちばん単純なモデル(M/M/1)では、処理時間をS、利用率をρとすると、平均の応答時間は S ÷ (1 − ρ) になる。
- 利用率50%: 20 ms
- 利用率70%: 33 ms
- 利用率80%: 50 ms
- 利用率90%: 100 ms
- 利用率95%: 200 ms
同じ10ポイントの上昇でも、効き方がまるで違う。50%→60%では20msが25msになるだけだが、85%→95%では約67msが200msになる。遅くなる量は約27倍だ。
台数を決める
ピーク時に毎秒3,000件が届き、1件あたりのCPU時間が20msのAPIを考える。
- 必要な処理量: 3,000 × 0.02秒 = 60コアが常に埋まる量。
- 目標の利用率で割る: 上限を60%とすると、60 ÷ 0.6 = 100コア。
- AZ障害に備える: 3つのAZのうち1つが落ちても残りで100コアを持つには、各AZ50コア、合計150コア。
150コアだと、平常時のピークでも利用率は40%しかない。障害の間だけ80%まで許すなら、残り2つのAZに75コアあればよく、合計114コアで済む。
- 100コア(余力なし): 90%(応答は約4倍)
- 114コア(障害時は80%まで許す): 約79%
- 150コア(障害時も60%を保つ): 60%
150と114のどちらを選ぶかは、技術では決まらない。障害中にどこまで遅くなってよいかという合意の問題だ。
平均ではなく、遅いほうの1%を見る
複数のバックエンドに並列で問い合わせ、全員の応答を待つ処理では、遅い1台が全体を遅くする。1台が遅い確率をp、台数をnとすると、遅くなるリクエストの割合は 1 − (1 − p)^n だ。
- 1台に問い合わせ: 1%
- 10台に並列: 9.6%
- 100台に並列: 63%
逆算すると、もっと厳しい。100台にファンアウトして全体の遅いリクエストを1%に抑えるには、1台あたりの遅い確率を約0.01%にする必要がある。全体のp99を守るには、各シャードのp99.99が同じ目標に収まっていなければならない。
- ファンアウトを減らす: データの置き方を見直し、1回で聞く台数を減らす。
- 打ち切る: 締め切りで切り、欠けた結果を扱える画面にする。
- ヘッジする: p95を過ぎても返らない要求だけ、別のレプリカにも投げる。追加の負荷は数%で済む(冪等な読み取りに限る)。
接続数を決める
DBから見た接続数は、台数 × プロセス数 × プールの大きさだ。アプリを横に増やすと、掛け算で増える。
- 平常時 10台: 200
- デプロイ中 新旧で20台: 400
- セールの日 40台: 800(上限超え)
プールの大きさもリトルの法則で見積もれる。1台に毎秒250件が届き、1件が接続を8ms握るなら、使うのは平均2本。DBが5倍遅くなっても10本で足りる。プールを20にしても、効くのはDBがすでに遅いときだけで、そのとき同時のクエリを増やすとDBはさらに遅くなる。
上限はDBの側から決める。HikariCPのWikiは「DBのコア数 × 2 + ディスク数」を出発点に挙げていて、16コアなら30本強だ。40台のアプリで分けると1台1本にも満たないので、台数が大きく変わる構成では、PgBouncerやRDS Proxyのような接続のプロキシを間に置く。
1枚の見積もりシート
| 項目 | 式 | この記事の例 |
|---|---|---|
| 同時処理数 | ピークの到着率 × p99の処理時間 | 500 × 0.2秒 = 100 |
| 必要な処理量 | ピークの到着率 × 1件のCPU時間 | 3,000 × 20ms = 60コア |
| 台数 | 必要な処理量 ÷ 目標の利用率 | 60 ÷ 0.6 = 100コア |
| AZ障害の余力 | 台数 × AZ数 ÷ (AZ数 − 1) | 100 × 3 ÷ 2 = 150コア |
| ファンアウトのテール | 1 − (1 − p)^n | p = 1%、n = 100 → 約63% |
| DBへの接続数 | 最大台数 × プロセス数 × プール | 40 × 1 × 20 = 800 |
面接での使い方
式を先に書いてから数字を入れると、面接官が途中で前提を確かめやすい。
最後の一文のように、余力の値段と、それを下げる選択肢まで自分から出すと、数字を覚えているのではなく、トレードオフで決めていることが伝わる。