キャッシュって、入れれば速くなる魔法に見えるんですよね。

でも計算機科学には、こんな有名な格言があります。「計算機科学で難しいことは2つしかない。キャッシュの無効化と、命名だ」(フィル・カールトン)——半分冗談、半分本気の警告です。

今回は、「キャッシュがあるのに落ちた」の正体から、その「難しい方」を8つ集めました。1つでも知らなかったら儲けものです。

① キャッシュの寿命が揃うと、全滅も揃います

デプロイ直後にキャッシュを一斉投入。TTLは60秒。きれいな設計に見えますよね。

60秒後、すべてのキーが同時に消滅します。その瞬間、全リクエストがキャッシュを素通りしてDBを直撃。キャッシュがあるのに、無いのと同じ負荷が一撃で来ます。

キャッシュスタンピード(殺到)と呼ばれる定番の障害で、対策はシンプルです。

TTLにランダムな揺らぎを足す。60秒±10秒にするだけで、消滅タイミングがばらけて、崖が坂になるんです。

② 人気キーの再生成は、1人にやらせます

①の親戚で、もっと局所的なやつです。

人気商品のキャッシュが切れた瞬間、待っていた1000リクエストが全員でDBに再生成しに行きます。答えは全員同じなのに、同じ重いクエリが1000本走る。

定石は、最初の1人だけが再生成し、残りは待つか古い値を使う仕組みです。「多重リクエストの排除(request coalescing)」と呼ばれます。

キャッシュが切れる瞬間の振る舞いまで決めて、初めて設計なんですよね。

③ 「存在しない」も、キャッシュします

意外と知られていない防御が、これです。

存在しないIDへのアクセスは、キャッシュに乗らないので毎回DBまで貫通します。攻撃者がランダムなIDを大量に叩けば、キャッシュを完全に素通りしてDBを殴れるわけです。

対策は、「存在しなかった」という事実そのものを短いTTLでキャッシュすること。ネガティブキャッシュと呼ばれ、DNSの世界では昔から標準装備です。

キャッシュは「あるもの」だけでなく「ないもの」も守備範囲なんです。

④ DB更新とキャッシュ削除、順番で整合性が変わります

データを更新するとき、DBとキャッシュのどちらを先に触るか。ここに答えがあります。

定石は、「DBを更新してから、キャッシュを削除する」です。

逆(先にキャッシュ削除)だと、削除とDB更新のわずかな間に他のリクエストが来て、古いDBの値を読んでキャッシュに書き戻してしまう。以後、TTLが切れるまで古い値が生き続けます。

なお、正しい順番でもタイミング次第で極めて稀な競合は残ります。だからTTLは保険として必ず付けておくのがセットです。

「更新したのに画面が変わらない、でもしばらくすると直る」——この症状を見たら、まずこの順番を疑ってください。

⑤ キャッシュの本職は、「速くする」より「守る」です

視点が変わる話です。

キャッシュヒット率99%のシステムでキャッシュが消えると、DBへの負荷は1%→100%、つまり100倍になります。DBは100倍に耐えられる設計になっていますか?

なっていないなら、そのキャッシュはもう「高速化のおまけ」ではなく、DBを守る防波堤=落ちてはいけない基盤です。

「キャッシュはいつ消えてもいいもの」という初期の前提が、成長したシステムでは崩れている。キャッシュ障害=全面障害になっていないか、一度は棚卸ししたい観点です。

⑥ サーバー内キャッシュは、台数ぶんの世界を作ります

各アプリサーバーのメモリ内にキャッシュを持つと、速いんですが副作用があります。

サーバーが10台あれば、10個の別々のキャッシュが存在します。更新を1台が反映しても、残り9台は古いまま。ユーザーはロードバランサ次第で、リロードするたびに新旧の値を行き来します。

「更新が反映されたり戻ったりする」という怪奇現象の正体は、だいたいこれです。

対策は、共有キャッシュに寄せるか、ローカルのTTLを短くして「ズレの上限」を仕様として明言するか。ズレをゼロにするより、ズレの幅を約束する方が現実的なんです。

⑦ ホットキーは、分散しても救えません

キャッシュサーバーを何台に増やしても、アクセスが1つのキーに集中していたら、そのキーを持つ1台に全負荷が乗ります。

セール対象の1商品、話題の1投稿。分散の仕組みはキーを台数に散らすので、「1つのキーの中」は散らせないんですよね。

対策は、キーを複製して読み分ける(同じ値をkey#1〜key#Nで持つ)、あるいはアプリ側の超短命ローカルキャッシュで受ける。

負荷分散は「キーの数」に効き、「キーの偏り」には効かない。この区別が設計の分かれ目です。

⑧ 何を入れるかは、3つの掛け算で決まります

最後は、判断基準の話です。

キャッシュに入れる価値は、「再計算の重さ × 読まれる頻度 × 鮮度のゆるさ」の掛け算で決まります。

向いているもの向いていないもの
再計算の重さ重い集計・結合軽い処理
読まれる頻度ランキング・マスタ情報めったに読まれないもの
鮮度のゆるさ多少古くてもいいもの1秒の古さも許されないもの(残高など)

向いていないものを入れても、複雑さが増えるだけの損になります。「とりあえずキャッシュ」をやめて、3つの数字で語る。設計会話が一段具体的になります。

番外編:知っておくと得する小ネタ

「古いのを出しながら、裏で更新」という技
HTTPには stale-while-revalidate という指定があります。期限切れのキャッシュを即座に返しつつ、裏で新しいものを取りに行く。ユーザーを待たせない更新の定番です。

CPUも同じことをしています
CPUのL1/L2キャッシュ、OSのページキャッシュ、CDN。コンピュータは上から下まで「近くに置いて速くする」の階層でできています。今日の原則は、全階層で同じなんですよね。

「キャッシュ消したら直った」の意味
それは直ったのではなく、④や⑥の不整合を症状ごとリセットしただけかもしれません。再発するなら、順番と共有方式を見るタイミングです。

RJCでも、キャッシュと向き合っています

RJCが携わる決済や交通のシステムは、ピーク時間の負荷が桁で跳ねる世界です。⑤の「キャッシュは防波堤」という視点は、そのまま可用性の設計なんですよね。

こういう仕組みの話を、Techチームで共有しながら開発しています。

よくいただくご質問

Q. まずどれから押さえるべきですか?
A. ④の更新順序かなと思います。コード1行の順番の話なのに、直せば「たまに古い値」がほぼ消えるんですよね。

Q. TTLはどれくらいが正解ですか?
A. ⑧の3つ目「鮮度のゆるさ」から逆算するのが正解です。「このデータ、何秒古くても業務が困らないか」を仕様として決めるのが先なんです。

Q. Redisとローカルキャッシュ、どちらを使うべきですか?
A. 整合性が大事ならRedisのような共有型、速度最優先で多少のズレを許せるならローカル+短TTLです。⑥のとおり「ズレの幅を約束できるか」で選ぶと迷いません。

Q. 未経験からでも、分かるようになりますか?
A. なります。キャッシュは「冷蔵庫に何を常備するか」と同じ発想なので、日常の感覚がそのまま設計に使える分野なんです。

消し方を決めてから、入れる

今日の8つに共通していたのは、これでした。

キャッシュは、入れるのは1行です。難しさは全部、消えるとき・消すとき・消し忘れたときに潜んでいます。

だから設計の順番は逆で、「いつ、どう消えるか」を決めてから入れる。格言が「無効化が難しい」と言っているのは、そういうことなんですよね。

みなさんが踏んだキャッシュの事故、よかったら教えてください。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?