🔢

エンジニアが直感的に覚えておくべき「数字」の教養

なぜ「数字の直感」が必要なのか

目的は、おおまかな見積もり(概算見積もり)ができるようになることです。システム設計の段階で「この構成でトラフィック量・応答速度・可用性の要件を満たせるか」を判断するには、細かい計測の前に桁レベルの当たりをつける力が要ります。

たとえば「想定QPSに対してDB1台で捌けるか」「レイテンシ要件をネットワークの往復回数が超えてしまわないか」といった問いへ、その場の概算で答えられるかどうかにより、設計の議論の速さと精度は大きく変わります。『システム設計の面接試験』(アレックス・シュウ著、ソシム)でも、個別の設計問題より先に、まずこの概算見積もり(back-of-the-envelope estimation)が扱われています。

大事なのは正確な計算ではなく「桁」を当てることです。この記事では、そのための土台になる3つの数字を整理します。2のべき乗、ジェフ・ディーンのレイテンシ表、可用性のダウンタイム換算です。

2のべき乗: データ量を瞬時に換算する

まずはよく見る2のべき乗からです。ざっくりとした対応を表にすると次のようになります。

べき乗おおよその値0の個数容量メディアの目安
2^10約1,00031KB日本語のテキスト約300文字
2^20約100万61MBスマホ写真1枚(圧縮後)
2^30約10億91GB動画10分程度(フルHD)
2^40約1兆121TB外付けHDD1台
2^50約1,000兆151PB大規模サービスの全ログ

覚えるべき近似は「2^10 ≒ 10^3(約1,000)」の1個だけです。あとは掛け算で導けます。2^20 = 2^10 × 2^10 ≒ 10^6(100万)、2^30 ≒ 10^9(10億)という具合です。厳密には2^10は1,024なので誤差がありますが、桁を当てる目的には十分な精度です。

この表が頭に入っていると、次のような問いに即答できます。

  • int64(8バイト)のIDを10億件持つと何GB? → 8バイト × 10^9 = 8GB。メモリに載る量
  • 1KBのレコードが1,000万件あるテーブルは? → 10^3バイト × 10^7 = 10GB

注意点として、テキストの見積もりでは1文字=1バイトとは限りません。UTF-8では英数字は1バイトですが、日本語は1文字3バイトです。1KBに入る日本語は約300文字、というのが表の目安の根拠です。

換算の実例: 画像アップロードのストレージ見積もり

次は、この表を使った実例です。お題は「1,000人のユーザーが毎日1枚ずつスマホ写真をアップロードするサービス。1年後のストレージ容量とコストはどれくらいか」です。

手順は「単位を揃える→桁だけ計算する→端数は捨てる」の3ステップです。

  1. 単位を揃える。スマホ写真1枚を約3MBとすると、1日あたり 1,000人 × 3MB = 3,000MB = 3GB
  2. 桁だけ計算する。1年を約400日と置くと、3GB × 400 ≒ 1.2TB。真面目に365日で計算しても約1.1TBなので、桁は合っている
  3. コストに換算する。Amazon S3(東京リージョン)は1GBあたり月約0.025USDなので、1TBで月約25USD、日本円で4,000円程

つまり「この規模なら1年間ため込んでも月数千円。ストレージ設計で悩む段階ではない」と判断できます。

逆にユーザーが100倍の10万人になると、1日300GB、1年で約110TB、ストレージ費用は月約2,750USD(約40万円)です。ここまで来ると、古い画像を安いストレージ層へ移すライフサイクルルールや、アップロード時の圧縮・リサイズといった設計判断が、数字で正当化できるようになります。

ジェフ・ディーンのレイテンシ表: 速度差を体で覚える

Googleのジェフ・ディーン博士が示した、典型的なコンピュータ操作にかかる時間の一覧です。数値は2010年時点のもので、ハードウェアの高速化により一部は古くなっていますが、操作ごとの速さ・遅さの桁感覚をつかむ上では今でも有効です。

コンピュータ操作名時間
L1キャッシュ参照0.5ns
分岐予測ミス5ns
L2キャッシュ参照7ns
ミューテックスのロック/アンロック100ns
メインメモリ参照100ns
1KBのzip圧縮10,000ns = 10μs
1Gbpsネットワーク上の2KB送付20,000ns = 20μs
メモリからの1MBの連続読込み250,000ns = 250μs
同一データセンター内の往復500,000ns = 500μs
ディスクのシーク10,000,000ns = 10ms
ネットワークからの1MBの連続読込み10,000,000ns = 10ms
ディスクからの1MBの連続読込み30,000,000ns = 30ms
カリフォルニア-オランダ間の往復のパケット送付150,000,000ns = 150ms

注: ns=ナノ秒(10^-9秒)、μs=マイクロ秒(10^-6秒)、ms=ミリ秒(10^-3秒)を表します。換算すると1ms=1,000μs=1,000,000nsになります。

参照: Google Pro Tip: Use Back-Of-The-Envelope-Calculations To Choose The Best Design

2020年時点の数値を可視化したものが次の図です。

Latency Numbers Every Programmer Should Knowの可視化

出典: Latency Numbers Every Programmer Should Know

実務で使える指標

この数値から、実務の見積もりにそのまま使える指標が導けます。

  • メモリの連続読み込みは約4GB/s、SSDは約1GB/s、HDDは約30MB/s、1Gbpsネットワークは約100MB/s
  • つまりメモリはSSDの約4倍、HDDの約120倍速い
  • 同一データセンター内の往復は毎秒約2,000回できる
  • 世界規模の往復は毎秒6〜7回が限界
  • L2キャッシュはL1の約14倍、メインメモリはL1の約200倍遅い

参照: Latency Numbers Every Programmer Should Know

表から導ける設計の定石

このレイテンシ表は、普段当たり前に使っている設計の定石の「根拠」になっています。

  • メモリはディスクより圧倒的に速い。1MBの連続読み込みで比べると、メモリ250μsに対してディスクは30msで、100倍以上の差がある。RedisやMemcachedのようなキャッシュを挟むだけで桁が変わるのはこのため
  • ディスクのシーク(10ms)を避ける。ランダムアクセスを繰り返すより、シーケンシャルに読み書きするほうが速い。Kafkaのような追記型ログの設計が高速なのはこの性質を突いているため
  • ネットワークの往復が支配的になる。データセンター内でも1往復500μsかかるので、N+1クエリのように往復回数が増える実装は、1回1回は軽くても合計で致命的になる。大陸間なら1往復150msなので、往復回数の削減やCDN・リージョン配置がそのままレイテンシ要件に直結する
  • 圧縮は安い。1KBのzip圧縮は10μsで、ネットワーク転送よりずっと軽い処理。転送前に圧縮するのが基本的に得なのは、この差があるため

可用性の境界線: 99.9%は「1日1.44分」

可用性を語るときによく使われるのがSLA(サービスレベルアグリーメント)です。サービスプロバイダーと顧客との契約で、サービスがどの程度の稼働率を実現するかを正式に定義するものです。稼働率は伝統的に「9の数」で測られ、9が多いほど良いとされます。9の数ごとの許容ダウンタイムを換算すると次のようになります。

可用性(%)1日のダウンタイム1週間のダウンタイム1月のダウンタイム1年のダウンタイム
99%14.40分1.68時間7.31時間3.65日
99.9%1.44分10.08分43.83分8.77時間
99.99%8.64秒1.01分4.38分52.60分
99.999%864.00ミリ秒6.05秒26.30秒5.26分
99.9999%86.40ミリ秒604.80ミリ秒2.63秒31.56秒

覚えておくと便利なポイントです。

  • AWS、Google Cloud、Microsoft Azureといった主要クラウドプロバイダーは、SLAを99.9%以上に設定している
  • 99.9%(スリーナイン)でも1日あたり1.44分しか止まれない。デプロイや再起動で数分止まる運用は、それだけでSLA違反になりうる
  • 99.99%(フォーナイン)になると1日8.64秒。人間が手動で対応できる時間ではなく、自動フェイルオーバーが前提になる
  • 9を1つ増やすごとに許容ダウンタイムは10分の1になり、実現コストは桁で増える

なお、SLAが顧客との契約であるのに対して、チーム内部の目標値はSLO(サービスレベル目標)と呼びます。GoogleのSREには、SLOまでの余裕(許容ダウンタイムの残り)をエラーバジェットと捉え、使い切るまでは積極的にリリースする、という考え方もあります。

依存が増えると可用性は掛け算で下がる

システム全体の可用性は、直列に依存するコンポーネントの掛け算で決まります。

可用性99.9%のサービスを2つ直列に並べると、0.999 × 0.999 ≒ 0.998で全体は99.8%になります。年間の許容ダウンタイムは8.77時間から約17.5時間へ、倍になる計算です。依存が10個に増えると0.999^10 ≒ 0.990で、1つ1つはスリーナインでも全体は99%、年3.65日止まる水準まで落ちます。マイクロサービスで同期的な依存を増やすことには、数字の上でこれだけの代償があります。

逆に、同じものを並列に冗長化すると可用性は上がります。99.9%のサーバーを2台並べて片方が生きていればよい構成なら、1 - (1 - 0.999)^2 = 0.999999で99.9999%です。「直列に並べると9が減り、並列に並べると9が増える」と覚えておくと、構成図を見ただけで可用性の桁が見積もれます。

まとめ

最後に、この記事で覚えるべき数字を3つに絞って再掲します。

  • 2^10 ≒ 10^3。これ1個で2^20=100万(1MB)、2^30=10億(1GB)まで全部導ける
  • レイテンシの代表値。メモリはμsの世界、ディスクとデータセンター間はmsの世界、大陸間往復は150ms
  • 99.9%は1日1.44分。9が1つ増えるごとにダウンタイムは10分の1、コストは桁で増える

これだけで、ストレージ見積もり、キャッシュを挟むかどうかの判断、SLAに対する構成の妥当性チェックが、その場の暗算でできるようになります。日々の設計レビューや障害対応の初動でも効いてくる教養だと思います。

参考文献

https://medium.com/@bojanskr/latency-numbers-every-programmer-should-know-d85f8d3f8e6a

新着記事

関連ネットワーク(beta)

ドラッグで移動 / Ctrl+ホイールでズーム