Momentoは、他の製品よりも使いやすく信頼性の高い、一貫して高速なパフォーマンスのキャッシュを提供するために、常に限界に挑戦し続けています。
しかし、キャッシュがすべての状況に適しているわけではないこと、Momentoのようなセントラル・キャッシュがキャッシュを必要とするたびに適切なソリューションとなるわけではないことを指摘することは重要です。キャッシュにはさまざまなタイプがあり、どのタイプのキャッシュを使うかは、あなたのニーズにマッチしたものでなければなりません。
このブログ記事では、3つの異なるタイプのキャッシュと、それらをどのような場合に使うべきかについて説明します。まず、キャッシュされたデータがどの程度集中化されているかに基づいてキャッシュを分類します。次に、中央キャッシュ、エッジ・キャッシュ、ローカル・キャッシュについて、中央集権型キャッシュと分散型キャッシュの異なる点を例として見ていきます。
集中型か分散型か:キャッシュの分散度は?
様々なキャッシュの比較を、機能(「このキャッシュはマルチセットをサポートしているか」)、性能(「毎秒何回のオペレーションを処理できるか」)、または運用モデル(「私のチームはこのキャッシュを管理する専門知識を持っているか」)に基づいて見つけることができます。しかし、これらはすべて似たようなタイプのキャッシュ(「セントラル・キャッシュ」)の比較であり、セントラル・キャッシュがあなたのニーズに適しているとは限りません。
キャッシュには他に「ローカル・キャッシュ」と「エッジ・キャッシュ」があります。キャッシュを使用する理由は様々ですが、最も一般的なものはユーザーへのレイテンシーを減らすことです。レイテンシを減らす最も明確な方法の一つは、アプリケーションがデータを取得するのに必要な距離を減らすことです。これがローカルキャッシュとエッジキャッシュの主な原動力です。
アプリケーション・クライアントがデータを取得する距離を短縮するために、多くの場合、一次ソースからデータをさらにプッシュする必要があります。クライアントがデータを取得するために単一の中央ロケーションに来るのではなく、データをいくつかのロケーションに分散させます。
しかし、コンピューターや人生におけるすべての選択と同じように、分散化にはトレードオフがあります。キャッシュはほとんどの場合、主要データの二次的な表現です。従って、一次データの更新は、キャッシュが陳腐で古いことを意味します。キャッシュの無効化の難しさは、コンピュータ・サイエンスで最も有名なジョークで強調されています。キャッシュされたデータを保持するノードの数が多ければ多いほど、データが変更されたときにそのデータを消去するのが難しくなります。
以下のセクションでは、セントラル・キャッシュ、ローカル・キャッシュ、エッジ・キャッシュの3つの異なるタイプのキャッシュについて説明します。以下では、セントラル・キャッシュ、ローカル・キャッ シュ、エッジ・キャッシュの3種類のキャッシュについて説明します。
この例では、次のような階層アーキテクチャを持つ標準的なSaaSアプリケーションを構築しているとします:
ユーザーはウェブ・ブラウザを使ってアプリケーションとやりとりを行います。ブラウザはHTTPリクエストを行い、ロードバランサーを経由してビジネス・ロジックを担当するアプリケーション・レイヤーに送られます。永続的なデータは、アプリケーション・レイヤーが使用するためにデータベースに保存されます。
このアーキテクチャの具体的な内容は実にさまざまです。ロードバランシングにApache Webサーバー、Linuxマシン上で動作するPHPアプリケーション、データレイヤーに信頼できるMySQLデータベースを備えたLAMPスタックを使用しているかもしれません。より近代的な “サーバーレス “アーキテクチャでは、ロードバランシングにAWS API Gateway、アプリケーション層にAWS Lambda、データベースにAmazon DynamoDBを使うかもしれません。
以下のセクションでは、異なるタイプのキャッシュをアーキテクチャのどこに、いつ配置するかを見ていきましょう。
セントラルキャッシュでアプリケーションを強化
キャッシュの最初の種類はセントラルキャッシュで、アプリケーションにキャッシュを追加することを検討するときに一般的に思い浮かぶタイプです。RedisやMemcachedのような伝統的なオプションと同様に、Momentoもセントラルキャッシュです。
その名の通り、セントラル・キャッシュはアプリケーション・サーバーやプライマリ・データベースの近くに設置されるます。パブリック・クラウドで運用している場合は、コア・インフラストラクチャと同じリージョンにある可能性が高くなります。
このアプリケーションの例では、セントラル・キャッシュは次のように表示されます:
ほとんどの場合、セントラル・キャッシュはプライマリ・データベースや他のサービスなど、他のシステムを補完するものとして使用されます。セントラル・キャッシュを使用することで、多くの利点を得ることができます。
中央キャッシュの主な利点は、アプリケーション全体の待ち時間を短縮することです。キャッシュはプライマリー・データベースとは基本的に設計が異なります。セントラル・キャッシュは、ディスク上の永続性を持たずにデータをメモリに保存する傾向があります。これは、プライマリ・データベースよりもデータ損失に対する安全性が低いことを意味するが、データのプライマリ・ストアになることを意図していないため、このトレードオフは受け入れられます。さらに、一般的にキーバリュー型のデータモデルを使用するため、柔軟なクエリパターンを使用することができません。
セントラルキャッシュは、プライマリデータベースや他のダウンストリームシステムの負荷を軽減し、可用性を向上させ、コストを削減するために使用することもできます。これは、DynamoDBのパーティション・スループット制限のように、同時リクエストを個々のアイテムに制限するような、他のシステムの特定の制限を回避するために行うことができます。または、キャッシュは、単純に任意のデータベースに読み取りスケーラビリティを追加するのに役立ちます。読み取りスケーラビリティを向上させるためにキャッシュを使用する場合、厄介なのはキャッシュのメタステーブルな動作を避けることです。
これから説明するように、セントラル・キャッシュの方が他のキャッシュよりもデータの無効化が簡単です。アプリケーションがプライマリ・データベースのデータを変更したとき、対応するキャッシュ項目を退避させるコマンドを発行することもできます。また、データベースにレコードが書き込まれるときにキャッシュにレコードを挿入することで、より簡単にデータをプロアクティブにキャッシュすることもできます。
さらに、中央キャッシュは大量のデータを保存するための最もダイナミックなオプションです。集中型のインメモリキャッシュは、極めて低いレイテンシで毎秒何十万ものリクエストを処理できるように設計されています。これらのキャッシュは、上で説明した明示的な無効化機能に加えて、一定期間後に自動的にアイテムを失効させるTTL(time-to-live)設定を備えています。データベースにレコードの正確なバージョンを保存することもできるし、集約や複数のレコードの組み合わせのような高価な計算の結果を保存することもできます。この柔軟性により、セントラル・キャッシュは様々なユースケースで利用されています。
セントラルキャッシュの最大の欠点は、運用負担の増加とセントラルキャッシュによる可用性への影響である。どのようなインフラストラクチャであっても運用上の負担はつきものであり、セントラルキャッシュをアーキテクトに追加する際にはその点を考慮しなければならない。さらに、キャッシュが停止した場合の上流のデータソースやアプリケーション全体の可用性への影響も考慮する必要がある。セントラルキャッシュを追加する前に考慮すべき主な点については、Meera Jindal氏の詳細なブログ記事を参照してください。
ローカルキャッシュによるネットワーク遅延の除去
ローカル・キャッシュでネットワーク・レイテンシを除去する2つ目のタイプのキャッシュは、集中化のもう一方の端に移動しましょう。ローカル・キャッシュは、キャッシュされたデータを一箇所に集中させるのではなく、データを必要とするクライアントのできるだけ近くに、分散化された方法でキャッシュされたデータを保存します。ローカル・キャッシュでは、必要なデータを、将来それを必要とする同じマシンに直接保存することができます。そうすることで、アプリケーションのワークフローで最も遅い部分であるネットワークリクエストをなくすことができます。
アーキテクチャの中でローカルキャッシュをよく見かける場所は2つあります。1つ目は、ユーザーのマシン上のスペースを借りることです。ブラウザのローカル・ストレージ、セッション・ストレージ、その他の組み込みのストレージ・メカニズムにデータをキャッシュすることができます。
ブラウザベースのストレージは、名前、ユーザー名、グループメンバーシップのような、ユーザー固有の情報を保存する良い方法です。これらは、アプリケーションのほとんどのページで使用される可能性があり、ローカルに保存することで、変化の遅いデータのためのネットワーク呼び出しの回数を減らすことができます。
ローカル・キャッシュを使用する2つ目の場所は、アプリケーション・サーバーです。各アプリケーション・サーバーは、下流のサービスへのネットワーク・リクエストを回避するために、データをメモリーまたはディスクに保存することができます。
ここでよくある例は、動きの遅い設定値やサービス発見情報をキャッシュすることです。AWS Lambda関数を使って構築している場合、関数の呼び出しにまたがってシークレットやその他の設定をキャッシュするのが一般的です。複雑な分散システムの例としては、DynamoDB の論文 (2022)を見ると、DynamoDBのリクエストルータがテーブルのメタデータをローカルにキャッシュしてデータ処理リクエストのレイテンシを削減していることがわかります。
設定値とは別に、アプリケーション・サーバは、応答時間を高速化するためにアプリケーション・データをキャッシュすることができます。CaffeineやGuavaのような一般的なJavaキャッシュライブラリは、この目的のためによく使われます。
ローカルキャッシュの主な利点は、ネットワークリクエストを完全に削除できることです。メモリから値を読み込むのは、確立された接続であっても、ネットワーク・リクエストをするよりも桁違いに速くなります。適切な状況であれば、ローカルキャッシュはパフォーマンスを向上させる最も簡単な方法となります。
もう1つの利点は、ローカルキャッシュを追加するのが簡単でコストがかからないことです。アプリケーションサーバーには、キャッシュされた値を保持できるメモリがあります。ユーザーのブラウザ上でキャッシュするのであれば、お金を払う必要はまったくありません!どちらの場合も、データをキャッシュすることは、既存のアプリケーションに数行のコードを追加するのと同じくらい簡単です。
しかし、ローカル・キャッシュはすべての状況に適しているわけではない。最大の問題は、その非中央集権性の機能です。基礎となるデータが変更されたときに、キャッシュされた値をプロアクティブにパージするのが難しい場合があります。このタスクは、基礎となるキャッシュハードウェアを制御している場合は難しく、ユーザーのブラウザにキャッシュデータを配布している場合はほぼ不可能です。したがって、キャッシュデータを保持する頻度と、キャッシュデータが古くなる可能性にどう対処するかについて、慎重に考える必要があります。
このため、ローカルキャッシュは、ブラウザのユーザー情報のように変化の遅いデータや、短時間のキャッシュでも高いキャッシュヒット率が得られる、頻繁にリクエストされる「ホットデータ」に最適です。
エッジキャッシュでユーザーの真ん中に
ローカル・キャッシュは素晴らしいが、その用途には限界がある。より頻繁に変更されるデータや、より多くのユーザーによって使用されるデータがある場合、キャッシュをもう少し集中化したいと思うでしょう。しかし、中央キャッシュから得られる完全な一元化は、グローバルに分散しているユーザーにとって大きなネットワーク遅延をもたらすかもしれません。
エッジキャッシュは集中化の中間点に位置します。エッジキャッシュでは、ネットワークのエッジにある複数の異なる場所にコンテンツを保存することになります。
エッジキャッシュで重要なのは、地理的な分布です。キャッシュされたデータを必要とするユーザーの近くに移動させるのです。ローカルキャッシュのように、ネットワークリクエストを完全に削除することはできません。しかし、ネットワーク・リクエストがレスポンスのために移動しなければならない距離を減らすことはできます。シカゴ、ベルリン、ムンバイにいるクライアントにバージニア北部にあるプライマリー・データベースを訪問させるのではなく、より近い場所にデータをキャッシュすることができます。
このアプリケーションの例では、エッジ・キャッシュは以下のように表示されます:
エッジキャッシュの価値を実証するために、異なる場所にいるユーザーを考慮するために図を拡張する必要があったことに注意してください。
最も一般的なエッジキャッシュのタイプは、コンテンツ・デリバリー・ネットワーク(CDN)で、画像(Snapchatを想像してほしい)、動画(Netflixを想像してほしい)、JavaScriptアプリケーション・バンドル(ブラウザでアクセスするすべての単一ページのアプリケーションを想像してほしい)などの静的ファイルを保存するために使用されます。もう1つよく使われるエッジキャッシュは、ドメインネームシステム(DNS)で、ウェブサイトのドメインを特定のサーバーにルーティングしてリクエストを処理するために使われます。
ユーザー向けのレイテンシはエッジキャッシュを使う理由の1つですが、コストと信頼性はもう1つの理由になります。ほとんどの製品では、エッジキャッシュを使用することは、Cloudflare、Fastly、Amazon CloudFrontのようなCDNプロバイダーを選択することを意味します。これらは世界中に多くの拠点を持つ、超高信頼性でリーズナブルな価格のシステムです。CDNにヒットするリクエストは、アプリケーションサーバーの負荷を軽減し、アプリケーションのより難しい側面に集中することができます。
エッジキャッシュはより集中化されているため、分散したローカルキャッシュよりも無効化が容易です。しかし、エッジキャッシュの無効化は高い確率で起こるとは期待されておらず、CDNの中には無効化のたびにお金を請求するものさえあります。よりダイナミックなキャッシュが必要な場合は、前述した一般的なセントラル・キャッシュに頼ることになるでしょう。
結論
キャッシュはレイテンシーを減らし、アプリケーション・アーキテクチャの負担を軽減する素晴らしい方法です。しかし、全てのキャッシュが同じではなく、使用例も様々です。この投稿では、キャッシュをエンドユーザーと比較してどの程度集中化されているかという観点から見てみました。その際、セントラル・キャッシュ、ローカル・キャッシュ、エッジ・キャッシュの3つの主要なキャッシュ・タイプを検討しました。
Momentoは、サーバーレス時代のために構築されたクラウドネイティブなセントラルキャッシュです。超高速パフォーマンス、即時プロビジョニング、フルマネージド運用モデルを提供します。VMベースのワークロードからサーバーレス、ファンクションベースのAPIまで、あらゆるタイプのアプリケーション・アーキテクチャに対応します。
今すぐ始めて、5分以内にアプリケーションにMomentoキャッシュを追加しましょう。キャッシュについて質問や議論がある場合は、Momento Discordに参加してください。