こんにちは。mediba advent calendar 2016の7日目担当 メディアシステム開発部の森です。
書きたいネタはあれど時間がかかりすぎてしまうので、
ひとまず箸休め的な記事として、Webサイトにおけるページキャッシュ設計の進め方について軽くまとめました。
第一線で開発しているエンジニアにとっては当たり前の事だと言われそうですが、
初心にかえってみようという趣旨であります。
Webサイトのある1ページを例に挙げ、キャッシュ設計の流れを(ちょっと雑に)追ってみたいと思います。
今回の例
LAMP環境(Smarty利用)
・ページ概要
要件1.会員制のサービスだが、非会員も利用できるコンテンツがある
要件2.会員と非会員で表示させたいコンテンツが違う(コンテンツはDBに格納されている)
要件3.会員状態は外部サーバに格納されており、提供APIにて取得
要件4.スマートフォン(iOS、Android)対応。
要件5.URLは一つで、会員状態やOSにより変わったりはしない
STEP1.ブラウザキャッシュの扱い
まずはブラウザキャッシュの扱いについて決めます。
ページの要件として、
- 動的要素が存在し、かつ重要
- 情報の鮮度が重要
- 情報の更新タイミングが重要
- ユーザーの状態(会員非会員といった要素)が重要
これらの要素がある場合、ブラウザキャッシュは使えません。
ブラウザキャッシュを使う場合、レスポンスヘッダの設定値だったりプロキシキャッシュ対策だったりと考えることは色々あります。
しかし使えない場合も ブラウザキャッシュを残さない 実装をきっちり考える必要があります。(意外と見落としがち)
今回の例の場合、要件2において 会員状態における表示コンテンツの違い という要素がありますので、基本的に 利用不可 です。
ブラウザキャッシュが存在すると、__非会員 → 会員にユーザー状態が変わった__にもかかわらず、キャッシュをクリアしない限りユーザーには 非会員状態のページ内容が表示 されてしまいます。
※ ブラウザキャッシュを残さない実装が甘い場合も同様です。
お金のやり取りが発生するサイトだと、結構Critcalな問題に発展する要素をもっています。
STEP2.ページの閲覧パターン整理
ブラウザキャッシュが使えない場合、サーバー側でキャッシュを用意する事を考えないといけません。
まずはユーザーステータスやOSといった出し分け要素を整理して、ページの表示パターン数をまとめます。
※Smartyを利用しているので、テンプレート(tpl)単位で切り分けています。
今回の場合、図のように最低でも4パターンのキャッシュを生成する必要があります。
OSでの出し分けに関しては、表示コンテンツに差異がない場合は別tplではなくCSS側で表示制御を巻き取って一つにするというやり方もあるかもしれませんが、OS依存・ブラウザ依存の闇は深いので やめておいた方が無難です。
STEP3.キャッシュできる情報があるかの整理
サーバー側でキャッシュできる情報がないかを整理します。
今回の例において、ページの表示内容を決定する最終的な要素は以下の3つです。
- コンテンツ
- 会員状態
- OS
今回の例ではどうしたかを、各要素ごとに簡単に書きます。
コンテンツ
コンテンツ情報をDBに問い合わせて取得する必要があります。
まず思い浮かぶのがクエリキャッシュですが、__DBサーバには問い合わせは発生__してしまいます。
DBサーバの性能如何によっては問題ないかもしれませんが、できればDBへのアクセス自体を少なくする方向にした方がよいでしょう。
ということで、今回の例においては膨大なデータ量でもないので 会員ステータスごとのコンテンツ情報をAPCuにキャッシュさせる 方向にしています。
会員状態
これに関してはWebサイトにもよりますが、会員・非会員状態はリアルタイムで取得することが理想です。
ただ、今回は要件3で挙げているように外部APIサーバへの問い合わせが発生します。
こちらのDBで会員状態をもっていれば工夫の仕様はあるのですが、この例においては 外部サーバが負荷に耐えられる事を信じる他ありません。
結論から言うと、最初から外部サーバが負荷に耐えられないことはわかっていたので__会員状態をKVSに格納する__方向にしました。
OS
最終的なtplです。ユーザーの会員状態とOSに対応したtplが、最終的な表示画面となります。
OSという括りをしていますが、PCとスマホ両方に対応している場合はデバイスも考慮に入れる必要があります。
STEP4.キャッシュの有効期間
STEP3で挙げた各要素に関して、それぞれのキャッシュの有効期間を決める必要があります。
コンテンツ
コンテンツ情報の更新頻度によって変わります。
1週間毎にしか更新されないのであれば、極論でいえばキャッシュの有効期間もその感覚で問題ないかもしれません。
実際には不測の事態でコンテンツ情報の更新が必要な事もあるかと思いますので、30分〜1時間あたりにしておけば不都合がないかもしれません。
会員状態
非会員 → 会員といった状態変更のための一連の操作と画面遷移が完了するまでに、最低でも何分かかるのかを踏まえて設定します。
状態変更操作に2分程度かかるのであれば、キャッシュの有効期間を60秒にしておく事で、限りなくリアルタイムに近い状態を維持でき、さらに外部APIへの問い合わせを少しでも減らすことができます。
OS
コンテンツのキャッシュ有効期間と矛盾が無いように決める必要があります。
コンテンツのキャッシュ有効期間を1時間に設定してあっても、最終的なtplキャッシュが30分に設定されていては意味がありません。
tplの有効期間をコンテンツより長くした上で、コンテンツが更新されたら裏でtplを削除する処理を仕込んだり、毎時0分に必ずtplを削除するなどといったアプローチが必要です。
STEP5.データの処理フローを整理する
図のように整理する事で、対象のページがどのようにキャッシュされて表示しているかを確認します。
青い枠の処理がキャッシュがある状態だとスキップされます。
ひとまずWebサーバ・DBサーバ・外部APIサーバにかかる負荷は軽減されることが予想されます。
まとめ
軽く流すつもりでだったのですが、進めていくうちに思いの外書くことが膨らんでいき、
結果的にすごく分かりにくい内容になってしまいました・・・。
次回があれば、キャッシュについて徹底的に調べてリベンジしたいです。