キャッシュヒット率の真実
キャッシュは CDN によって提供される主要なサービスの1つであり、CDN のパフォーマンスを評価する最も一般的な指標の1つがキャッシュヒット率 (CHR) です。長年にわたり CDN のお客様は、CDN がどの程度ユーザーに優れたサービスを提供し、トラフィックを処理しているかを判断する主要な指標として CHR を使用してきました。ダッシュボードに「キャッシュヒット率98 %」と表示されることも珍しくなく、その場合、エンドユーザーが CDN のメリットを最大限に得ていると簡単に納得しがちです。
しかし CHR は目に見える以上にはるかに多くの意味を含んでおり、私たちが普段、重視しているこの指標は私たちが考えていることとは別のことを意味している可能性があります。そこで今回は、CHR が実際に測定している中身について解説し、それを計算して評価する新しい方法の必要性について考察したいと思います。
従来の CHR の計算方法
長年にわたり、CDN は以下の式を使用して CHR を計算してきました。
requeststotal は CDN によって受信されたクライアントリクエストの合計数で、requestsorigin はそのうちオリジンに到達したリクエストの数です。簡単に言えば、CDN に100件のリクエストを送信し、そのうちの1件が CDN を離れてオリジンに到達した場合、CHR は99 %になります。
「キャッシュヒット率99 %」と聞くと、ユーザーのリクエストの99 % が CDN のエッジ (リクエストを実行したクライアントに最も近いキャッシュ) からコンテンツを提供されていると本能的に考えがちですが、実際には必ずしもそうであるとは限りません。その理由を理解していただくために、CDN アーキテクチャ、キャッシュ階層、ロングテールコンテンツについて説明します。
CDN のしくみ
あまり細かい話をするつもりはなく、もちろんすべての CDN について語ることはできませんが、最も基本的な意味で CDN はプロキシをキャッシュする分散ネットワークのことです。それらのプロキシが共有されることで キャッシュが共有されます。プロキシは CDN のお客様に属する、すべての多数のドメインにわたってリクエストを受信し、キャッシュします。これは、各サーバーがストレージも共有していることを意味します。ストレージは有限であるため、通常はストレージを管理するための削除モデルを含む、何らかのアルゴリズムが使用されています。頻繁にリクエストされるオブジェクトは、(有効期限を過ぎない限り) より長期間キャッシュ内に留まる傾向があり、あまり頻繁にリクエストされないオブジェクトは、たとえ Cache-Control
ヘッダーによって長期間キャッシュされている必要があると指定されていてもキャッシュから削除される傾向にあります。
ただし、ストレージ管理機能によってまだ有効期限を過ぎていないオブジェクトが1つのキャッシュサーバーから削除されたからといって、必ずしも CDN 全体とすべてのキャッシュサーバーから削除されるとは限りません。CDN には通常、何らかの階層モデルがデプロイされており、クライアントが通信しているサーバーのキャッシュの中にオブジェクトがない場合、そのオブジェクトをオリジンからフェッチしようとする前に、そのピアまたは親にオブジェクトを要求する傾向があります。問題は、そのピアや親が、クライアントが接続されているサーバーの横に常にあるとは限らないことです。そのため、オブジェクトをフェッチしてクライアントに配信するまでにかかる時間が長くなる可能性があります。以下の図をご覧いただくとわかりやすいと思います。
エッジキャッシュがクライアントからリクエストを受け取ると、クライアントとその特定のマシンの間に TCP 接続が確立されていても、レスポンスは至るところにあるストレージから送信される可能性があり、ストレージはそのサーバー上にあるとは限りません。オブジェクトはそのマシンのメモリ (最良のシナリオ)、そのマシンのディスクストレージ (SSD が望ましいです。そうでない場合、余分な負荷がかかります)、ローカルのピア/親、ローカルでないピア/親から提供される可能性があります。そして、オブジェクトの提供元がネットワークエッジから遠ざかれば遠ざかるほど、チェーン全体を通じてパフォーマンスが低下します。
究極的には通常、オブジェクトの提供元はそのオブジェクトがフェッチされる頻度と直接関係します。リクエストの頻度が低いほど、クライアントが接続されているエッジキャッシュでミスとなる可能性が高くなります。言い換えれば、ロングテールコンテンツ (ソーシャルメディアの投稿、プロフィール画像、e コマースサイトの大きなインベントリなど、あまり頻繁にフェッチされないコンテンツ) も CDN から提供される可能性はありますが、必ずしもクライアントの近くのキャッシュからとは限りません。
しかし、ほとんどの CDN は CHR を計算する際、オリジンに到達していない限り、いずれかのキャッ シュから受け取ったレスポンスをすべて「ヒット」と見なします。これが CHR が指標として誤解を招き、CDN が顧客に提供しているパフォーマンスに関して誤った認識をもたらす原因です。また、これは CDN のオブジェクトストレージモデルが重要な理由でもあります。エッジに十分な密度がない、エッジで適切なスケーリングが行われない、最適化された削除アルゴリズムがない環境でデプロイされている場合、CHR は高く見えますが、ロングテールコンテンツの多くが CDN の奥まったところから提供される可能性があります。
CHR のより望ましい計算方法
従来の CHR の計算方法では分からない部分があるため、私たちにとって重要なこの指標の計算方法を再考する必要があります。パフォーマンスのインジケーターとなる指標として、私たちが本当に知りたいのは、ネットワークのエッジにある CDN キャッシュから提供されるオブジェクトの割合です。以下ような計算式になります。
hitsedge と missesedge は、それぞれネットワークエッジでのキャッシュヒット数とキャッシュミス数を表します。この CHR の計算式では、ユーザーから最も近い、ネットワークのエッジから提供されたキャッシュ可能なリクエストの割合が正確に分かります。
実際には従来の計算方法はいまだに非常に有用です。オリジンに到達しないリクエストの割合がわかるため、サーバーのオフロードを測定するのに非常に便利な指標です。しかし、パフォーマンスを評価するには、エッジで何が起きているのかに注目する必要があります。最善の方法は、以下のように2つの異なる CHR 指標を考慮することです。ひとつはエッジにおける CHR です。
もう1つは全体的な CHRです。
CHRedge はパフォーマンスを測定する指標で、CHRglobal はオフロードを測定する指標です。両方とも貴重な指標で重要なインサイトをもたらしますが、それぞれ意味する内容は異なります。CHRedge はユーザーに近い、ネットワークのエッジから配信されているコンテンツの割合を測定します。これは直接、パフォーマンス上のメリットに換算されます。一方 CHRglobal は、オリジンに到達しないトラフィックの割合を示します。これは直接、プロセスとインフラストラクチャのオフロードに換算さ れ、コストの大幅な節約につながる可能性があります。
Fastly でキャッシュヒット率を計算する
Fastly のネットワークでは、すべての配信拠点 (POP) をエッジに配置して構築されており、Fastly のネットワークマップですべてのエッジのロケーションを確認できます。拡張性とストレージの密度を高めるため、各 POP 内にお客様やエンドユーザーのリクエストに対して透過的な、キャッシュ階層のレイヤーを設けています。これにより、あるリクエストがクライアントが接続されているサーバーではキャッシュミスであっても、それが通信している POP の内部のキャッシュからコンテンツが提供される可能性が高く、その場合エッジではキャッシュヒットになります。また、キャッシングの追加レイヤーとしてオリジンシールドをデプロイすることで、全体のキャッシュヒット率を高め、オリジンへのトラフィックを削減できます。
コントロールパネルを介して、または Stats API を通じて報告される CHR は以下のように計算されます。
オリジンシールドを使用しないサービスでは、これは CHRedgeと CHRglobaoの両方に事実上相当します。すべてのヒットはエッジにおけるヒットであり、すべてのミスはエッジにおけるミス (オリジンへのリクエスト) であるためです。
サービスでオリジンシールドが使用されている場合、つまりシールド POP によって別のレベルのキャッシュが追加される場合、上記の数式は成立しますが、ヒット数/ミス数にはシールドのヒットやミスも含まれます。つまり、一部のリクエストが2回カウントされるハイブリッドな測定基準です。シールドを使用するサービスの CHRedge と CHRglobal を分離し、それぞれ独立して計算する方法はいくつかありますが、私は通常 Fastly のリアルタイムログストリーミングを使用しています。
以下の VCL スニペットで、複数のロ ーカル変数を使用した CHR の計算用のログストリーミングをセットアップします。
sub vcl_log {
#FASTLY log
declare local var.cache_statusSTRING;
declare local var.request_typeSTRING;
set var.cache_status = if(fastly_info.state ~ "HIT($|-)", "HIT", if(fastly_info.state ~ "PASS($|-)", "PASS", "MISS"));
set var.request_type = if(req.http.fastly-ff,"reqUrl": "/",
log {"syslog req.service_id logging_endpoint :: "} var.cache_status "," var.request_type "," server.datacenter;
}
ここでは、ローカル変数 cache_status
は HIT
、MISS
、PASS
のいずれかになります。PASS
はキャッシュ可能なオブジェクトではないため計算から除外します。ローカル変数 request_type
は、コンテンツがシールド POP またはエッジ POP (または Fastly の別の POP ではなく、クライアントと直接通信するシールド POPで機能的にはそのリクエストのエッジ POPとなる) のいずれかから提供されたリクエストであることを示します。次に cache_status
、request_type
、3文字の POP コードを示す server_datacenter
を記録します。
この VCL では、ログエントリは以下のように表示されます。
Jan 7 23:15:26 cache-sjc3131 logging_endpoint[218999]: HIT,edge,SJC
Jan 7 23:15:33 cache-sjc3638 logging_endpoint[348011]: HIT,edge,SJC
Jan 7 23:15:40 cache-iad2120 logging_endpoint[342933]: MISS,shield,IAD
Jan 7 23:15:40 cache-sjc3644 logging_endpoint[341583]: MISS,edge,SJC
Jan 7 23:16:07 cache-iad2127 logging_endpoint[342933]: HIT,edge,IAD
Jan 7 23:16:24 cache-iad2127 logging_endpoint[14817]: MISS,edge,IAD
Jan 7 23:16:40 cache-iad2120 logging_endpoint[218999]: HIT,shield,IAD
Jan 7 23:16:40 cache-sjc3624 logging_endpoint[438579]: MISS,edge,SJC
Jan 7 23:23:25 cache-iad2122 logging_endpoint[342933]: PASS,shield,IAD
Jan 7 23:23:25 cache-sjc3140 logging_endpoint[348011]: PASS,edge,SJC
これで、これらのログ行から CHRedge と CHRglobal を簡単に計算できます。
以下の意味になります。
hitedge は
cache_status = “HIT”
かつrequest_type = “edge”
のログエントリです。
missedge は
cache_status = “MISS”
かつrequest_type = “edge”
のログエントリです。
同様に
以下の意味になります。
missshieldPOP は、
cache_status = ”MISS”
かつserver.datacenter = <your_shield_pop>
のログエントリ
hitedge は
cache_status = “HIT”
かつrequest_type = “edge”
のログエントリです。
missedge は
cache_status = “MISS”
かつrequest_type = “edge”
のログエントリです。
バージニア州ダレス (IAD) の POP をオリジンシールドとして使用する上記のサンプルログエントリを例に以下を計算します。
そして
これは、なぜ2つの指標がそれぞれ異なる内容を意味し、別々に評価される必要があるのかを示すシンプルかつ良い例です。
ログストリーミングを使用することで、シールドを使用するサービスの2つの指標を簡単に分離できますが、今後、Fastly の統計情報システムを通じてこれらの指標を個別に報告し、より簡単に利用できるようにすることに取り組んでいます。
最後に
Fastly では、エッジですべきことや、なぜそれがエンドユーザーにとってメリットがあるのかについてよく話し合います。キャッシュは今後もコア機能の1つであり、CDN がどれほど効率的にエッジでコ ンテンツをキャッシュしているか、常に注意する必要があります。それには CDN がキャッシュヒット率を報告する方法と、それがコンテンツやエンドユーザーのエクスペリエンスにとってどのような意味があるのかを理解することが重要です。エッジとグローバルの両方でキャッシュヒット率を計算することで、コンテンツがどのように提供されているのかを本当の意味で理解することができます。