キャッシュヒット率
キャッシュを入れたら速くなった気がする、では次の判断ができません。置ける量を増やすのか、寿命を延ばすのか、そもそもキーの作り方が悪いのか。手を動かす前に、いま効いているかどうかを数字にします。
効いている気がする、では次が決められない
キャッシュは、一度取ってきたものを手元に置いておく仕組みです。次に同じものを聞かれたとき、手元にあればそれを返します。これが hit、無くて元まで取りに行くのが miss です。全部のアクセスのうち何回当たったかの割合がヒット率で、キャッシュの効きを語るときの共通の物差しになります。
当たった回数を、当たった回数と外れた回数の合計で割ります。そのままだと 0.75 のような比率になるので、100 を掛けてパーセントにします。人に見せる数字は 75 のほうが判断しやすく、監視の閾値も置きやすいからです。
Python
print(3 / 4) # 0.75
print(3 / 4 * 100) # 75.0この割合は、CPU の中の小さなキャッシュから、ブラウザが持っている画像の控え、CDN の配信、DB の問い合わせ結果まで、どの層でも同じ式で測れます。層が違っても物差しが同じなので、どこが効いていないのかを並べて比べられます。
分母が 0 になる日がある
まだ一度もアクセスが来ていないとき、合計が 0 になります。Python は ZeroDivisionError で落ち、JavaScript は NaN を返します。落ちるほうがまだ幸せで、NaN は止まらずに画面やグラフまで運ばれていきます。
JavaScript
console.log(0 / 0); // NaN
console.log(NaN === NaN); // falseしかも NaN は自分自身と比べても等しくならないので、後ろの処理で見つけて弾くのも面倒です。割り算に入る前に、分母が 0 のときは何を返すかを決めておきます。平均を出す関数でも、形はまったく同じです。
Python
def averageScore(scores):
if len(scores) == 0:
return 0.0
return sum(scores) / len(scores)先頭の 2 行が、割り算を守る番人です。ここを書く癖が付いていると、割合を扱う関数で事故らなくなります。デプロイ直後やテスト環境のように、アクセスがまだ 1 件も無い状態は普通に訪れます。
85% でも遅いことがある
ヒット率は高いほうがよいのですが、それだけでは判断できません。外れたときの処理が重ければ、10 回に 1 回の miss が待ち時間の大半を占めます。ヒット率は、応答時間や元データへのアクセス数と並べて初めて意味を持ちます。
数字が下がったときに疑う先は、置ける量が足りない、置いたものがすぐ捨てられている、キーが細かすぎて同じ内容が別物として扱われている、のいずれかです。Redis なら INFO stats に当たりと外れの回数がそのまま出ているので、いま書いている割合の計算をそのまま当てはめれば、本番のヒット率が出せます。
要件
- cacheHitRate という名前の関数を実装すること
- ヒット率をパーセントで返すこと (100 倍する)
- hits + misses が 0 の場合は 0 を返すこと
入出力例
cacheHitRate(85, 15) → 85
cacheHitRate(1, 1) → 50
cacheHitRate(0, 10) → 0
cacheHitRate(10, 0) → 100
cacheHitRate(0, 0) → 0