現在、Web上にはたくさんのWebサイトが存在しています。見る側も昔と比べ格段に増え、現在はスマホをもつ世界中の人々がアクセスしてきます。
そのため、なにかしらWebサイトを立ち上げる際には現在はCDNなどを使用したキャッシュの仕組みが欠かせません。このエントリではWebサイトのケースによってどのようにキャッシュをとり負荷を減らすことができるのかを整理しつつまとめていきます。
Webサイトのキャッシュ
Webサイトをキャッシュする方法は数種類あります。
- CDNなどを使用したページ全体、コンテンツ全体のキャッシュ
- Redisなどのインメモリデータベースを用いたキャッシュ
- ブラウザ側に保存するキャッシュ
他にもあるかもしれませんが、ぱっと思い浮かべるキャッシュはこのあたりかと思います。それぞれの方法にはメリットやデメリットが存在するのでどれが正解ということはなく必要なケースに応じて選択する必要がありそうです。
CDNなどを使用したキャッシュ
多分、現在のWebサイトではほとんどがCDNを通っているかと思います。有名なCDNサービスとしては Cloudflare や Akamai 、 Fastly 、 CloudFront などでしょうか。
WebサイトでこれらのどのCDNを使っているかというのは簡単に調べることができます。レスポンスのHTTPヘッダーにServer
やVia
がついており、例えばCloudflareはServer: cloudflare
やCf-Cache-Status
などが付与され、Fastlyなんかは内部でVarnish*1というOSSのHTTPアクセラレーターを使っているためVia: 1.1 varnish
が付与されます。
このようなCDNのキャッシュ制御はExpires
なども使えますが基本的にはCache-Control
ヘッダーを使うかと思います。詳しくはMDNを読むといいでしょう*2。
CDNのキャッシュのみを制御するなら、s-maxage
が一番良いです。
Redisなどのインメモリデータベースを用いたキャッシュ
GraphQLのキャッシュや、APIのキャッシュなどでよく使用されるイメージです。RDSで取得する場合SELECT
するとやはり時間がかかるし高負荷になった際に大変になります。RDSなどはレプリケーション*3をしてレプリカインスタンスから読むという方法もありますが、GraphQLで同じ情報を複数取得するなどの場合は一度インメモリデータベースに入れてしまうことでパフォーマンス改善することができます。
ブラウザ側に保存するキャッシュ
Cache-Control
ヘッダーでmax-age
を指定したりETag
ヘッダーを付与しておくことでブラウザ側でキャッシュすることが可能です。プライベートキャッシュが正式名称っぽい。変更しない画像などに設定しておくことでリクエストが飛ばない(一部飛ぶ)ので表示も早くなる可能性があります。
ケースに応じたキャッシュ
CDNのキャッシュ設定はWebサイトのケースに応じた設定をすると良いです。ケースに応じた設定とは、ピークタイミング、認証が必要か、リアルタイムで更新が必要かどうかなどです。
ピークタイミングは全体的に緩やかで認証は必要ないケース
ブログやまとめサイトのようなケースになると思います。こういう場合はキャッシュは長くても短くても問題ないかと思います。しかし、記事の更新時などにはうまくキャッシュを更新する仕組みが必要です。
考えられる方法としては、
- キャッシュ間隔を1分など短い間隔にしておく
Cache-Control: s-maxage: 60, stale-while-revalidate
- 記事更新された場合にパージする
- 記事執筆者だけキャッシュ無しで見せて他は5分ほどのキャッシュにする
があるかと思います。一番簡単なのは1つめですが1分おきにリクエストがあれば都度オリジンにアクセスが来るのでここの調整は難しいです。3番目の執筆者だけキャッシュをしない方法は zenn.dev がやられている方法*4です。CDNでやるなら、セッション用Cookieをキャッシュキーに追加し、オリジン側ではCookieがついている場合はCache-Control: no-store
とかにすると実現できると思います。
2番目のパージは基本的にCDNはできないと考えたほうがいいです。もしどうしてもやりたい場合は、パージが高速に行われるVarnishを直接EC2などで動かしてつかうかFastlyを使ってAPI経由でパージするかです。ちなみに、FastlyのAPIパージは0.1秒ぐらいでできるので結構体験は良いです。
ある一定時間だけアクセス数が爆増する(スパイクする)ケース
Appleのイベント後の購入ページとかが一番わかり易いかなと思います。多分あれめちゃくちゃアクセス来ます。
基本的にCDNから配信されていれば耐えられると思いますが、もし1分だけ爆増し、その時間がわかっている場合などではそのタイミングでキャッシュの期限が切れてオリジンへのアクセスが来ないように気をつけるといった対策も有効です。また、Redisなどに事前キャッシュを作っておくという手も存在します。
リアルタイム性が求められるケース
この場合はキャッシュをすることが難しくなります。方法としてはページレイアウトなどキャッシュできる部分はキャッシュし、リアルタイム性を求める部分はAPIとするのが一般的かと思います。HTMLの一部分だけキャッシュするという方法が ASP.NET に存在しますがぶっちゃけ混乱しそうというのが私の考えです*5。できるだけ構成をシンプルにするのに越したことはないと思います。
最近ではAbema.tvがワールドカップの配信でCDNの活用が話題になっていました。Abemaではm4s
というストリーミング形式のファイルを配信することでストリーミングさせているようで、これらのファイルをCDN通すことでキャッシュしています。そのため、数秒から数分の遅延が生じるのかと思われます。
まとめ
キャッシュは楽しい。
説明が間違っている箇所を見つけた場合、Twitterやはてブなどでご指摘ください。