web-dev-qa-db-ja.com

キャッシングとは何ですか?

私は、キャッシングによって解決されたパフォーマンスの問題xを抱えていた人yについて常に聞いています。

または、プログラムコードでx、y、zを実行すると、キャッシュ機能が損なわれる可能性があります。

最新のポッドキャストの1つでも、ジェフアトウッドは、迅速な取得のために特定の値をキャッシュする方法について話しています。

「キャッシュ」と「キャッシング」という用語にはあいまいさがあるようで、さまざまなケースでの意味について混乱するようになりました。アプリケーションまたはデータベースのキャッシュ、CPUなどを参照しているかどうか、およびその意味。

キャッシングとは何ですか?異なるタイプは何ですか?

コンテキストから、私はそれの感覚をつかむことができます。多くの場合、取得した値をメインメモリに保存し、クイックルックアップアクセスします。しかし、それは何ですか本当に

このWordは、わずかに異なる意味(CPU、データベース、アプリケーションなど)で多くの異なるコンテキストで使用されているようです。

アプリケーションでのキャッシュの動作とデータベースのキャッシュの違いはありますか?

キャッシュを傷つけるコードを見つけたと誰かが言うと、それを修正した後、アプリの速度が向上しました。

プログラムのキャッシングは自動的に行われますか?プログラムで値をキャッシュするにはどうすればよいですか?このサイトのユーザーは、アプリケーションに値をキャッシュしたと言っていることがよくあります。ここに座って、何を意味するのか疑問に思います。

また、誰かがデータベースのキャッシュについて話すとき、それは本当に何を意味しますか?これは単に、データベースで有効にする機能ですか?値を明示的にキャッシュする必要がありますか、またはデータベースがキャッシュする値を選択しますか?

パフォーマンスを改善するためにアイテムを自分でキャッシュする方法は?

applicationsで値のキャッシュを開始する方法の例をいくつか教えてください。または、これは既に行われているもので、ボンネットの下にあり、「キャッシング」を許可する特定の方法でコードを記述する必要がありますか?

データベースのキャッシュについてはどうですか、それをどのように始めるのですか? memcacheのようなことを聞​​いたことがあります。データベースにキャッシュするには、このタイプのユーティリティが必要ですか?

私は、アプリケーションとデータベースのキャッシュ、それらの使用方法、および両方のケースでの実装方法を明確に区別したいと考えています。

65
KingNestor

キャッシュとは、明示的または暗黙的に、高性能ストア(通常はメモリ)にデータを格納し、そこからデータを取得することです。

説明させてください。メモリは、ファイル、リモートURL(通常)、データベース、またはその他の情報の外部ストアよりも高速にアクセスできます。したがって、これらの外部リソースのいずれかを使用する行為が significant である場合、パフォーマンスを向上させるためにキャッシングの恩恵を受けることができます。

クヌースはかつて、時期尚早な最適化がすべての悪の根源だと言っていました。まあ、私が懸念している限り、早すぎるキャッシングはすべての頭痛の種の根源です。問題を解決するまでがある問題を解決しないでください。あなたが下す決定はすべて、今それを実装し、後でそれを変更するために再度支払うために支払う費用がかかります。

したがって、最初に実際に問題があることと、それがどこにあるかを特定します。ここでは、プロファイリング、ロギング、およびその他の形式のパフォーマンステストが役立ちます。このステップがどれほど重要かを強調することはできません。人々が問題ではない何かを「最適化」するのを見た回数は驚異的です。

わかりましたので、パフォーマンスの問題があります。ページに長時間かかるクエリを実行しているとします。読み取りの場合、いくつかのオプションがあります。

  • クエリを別のプロセスとして実行し、結果をキャッシュに入れます。すべてのページは単にキャッシュにアクセスします。キャッシュバージョンは、必要に応じて何度でも更新できます(1日に1回、週に1回、5秒ごとに1回、適切な場合は何でも)。
  • 永続性プロバイダー、ORMなどを介して透過的にキャッシュします。もちろん、これは使用しているテクノロジーによって異なります。たとえば、HibernateとIbatisはクエリ結果のキャッシュをサポートしています。
  • 結果がキャッシュにない場合(または「古い」、つまり、指定された「年齢」よりも前に計算されることを意味する)、ページにクエリを実行させ、キャッシュに入れます。これには、2つ(またはそれ以上)の別個のプロセスがすべて結果を更新する必要があると判断した場合に、同じ(高価な)クエリを一度に8回実行するという並行性の問題があります。キャッシュをロックすることでこれを処理できますが、別のパフォーマンスの問題が発生します。また、言語の並行処理メソッドにフォールバックすることもできます(例Java 5つの並行処理API))。

更新の場合(または、更新が読み取りキャッシュに反映される必要がある場合)、キャッシュに古い値を設定し、データベースに新しい値を設定してページを提供するのは良くないため、少し複雑になりますデータの一貫性のないビュー。しかし、大まかに言えば、これには4つのアプローチがあります。

  • キャッシュを更新してから、関連するストアを更新する要求をキューに入れます。
  • キャッシュスルーライト:キャッシュプロバイダーは、変更が行われるまで更新を保持し、呼び出し元をブロックするメカニズムを提供します。そして
  • ライトビハインドキャッシュ:ライトスルーキャッシュと同じですが、呼び出し元をブロックしません。更新は非同期で個別に行われます。そして
  • サービスとしての永続モデル:これは、キャッシュメカニズムが何らかの種類の観察可能性をサポートしていることを前提としています(つまり、キャッシュイベントリスナー)。基本的には、完全に独立したプロセス(呼び出し元には不明)がキャッシュの更新を待機し、必要に応じてそれらを保持します。

上記のどの方法を選択するかは、要件、使用しているテクノロジー、その他の要因のホスト全体に大きく依存します(たとえば、クラスタリングとフェールオーバーのサポートが必要ですか?)。

それ以上に具体的であり、あなたの問題に関する much 詳細(問題があるかどうかなど)を知らずに何をすべきかについてのガイダンスを与えることは困難です。

51
cletus

ほとんどの場合、Webアプリケーションのコンテキストでのキャッシングについて読むことになります。 Webの性質上、キャッシュはパフォーマンスに大きな違いをもたらす可能性があります。

以下を考慮してください。

Webページ要求はWebサーバーに到達し、Webサーバーは要求をアプリケーションサーバーに渡します。アプリケーションサーバーはページをレンダリングするコードを実行します。

このモデルは、ページに対するリクエストの数が増えると、サーバーがリクエストごとに同じことを何度も繰り返す必要があるため、うまくスケールしません。

Webサーバー、アプリケーションサーバー、およびデータベースが異なるハードウェア上にあり、ネットワークを介して相互に通信する場合、これはさらに問題になります。

多数のユーザーがこのページにアクセスしている場合、すべてのリクエストでデータベースにアクセスしない方が理にかなっています。代わりに、異なるレベルでのキャッシュに頼ります。

結果セットキャッシュ

結果セットのキャッシュは、データベースクエリの結果をアプリケーション内のクエリとともに保存します。 Webページがクエリを生成するたびに、アプリケーションは結果が既にキャッシュされているかどうかを確認し、キャッシュされている場合は、代わりにメモリ内のデータセットから結果を取得します。アプリケーションはまだページをレンダリングする必要があります。

コンポーネントキャッシュ

Webページは、ページレットなどのさまざまなコンポーネントで構成されています。コンポーネントのキャッシュ戦略では、コンポーネントを要求するために使用されたパラメーターを知っている必要があります。たとえば、サイトの小さな「最新ニュース」バーでは、ユーザーの地理的な場所または好みを使用してローカルニュースを表示します。したがって、場所のニュースがキャッシュされている場合、コンポーネントをレンダリングする必要はなく、キャッシュからプルできます。

ページキャッシュ

ページ全体をキャッシュするための戦略の1つは、完全にレンダリングされたHTMLとともにクエリ文字列やヘッダーパラメーターを格納することです。ファイルシステムはこれに十分高速です。Webサーバーがファイルを読み取る方が、アプリケーションサーバーを呼び出してページをレンダリングするよりもずっと安価です。この場合、同じクエリ文字列を送信するすべてのユーザーは、同じキャッシュコンテンツを取得します。

これらのキャッシング戦略をインテリジェントに組み合わせることが、多数の同時ユーザー向けの本当にスケーラブルなWebアプリを作成する唯一の方法です。簡単にわかるように、ここでの潜在的なリスクは、キャッシュ内のコンテンツがそのキーによって一意に識別できない場合、人々が間違ったコンテンツを見始めることです。特にユーザーがセッションを持ち、セキュリティコンテキストがある場合、これはかなり複雑になる可能性があります。

14
cdonner

私が知っている2つの意味があります。


1つは、アプリケーションキャッシングです。これは、データがどこか(ネットワーク経由など)から取得するのが遅い場合、または計算が遅い場合、アプリケーションがデータのコピーをキャッシュするためです(再取得または再計算する必要がありません:既にキャッシュされています)。キャッシュを実装するには、追加のアプリケーションソフトウェア(キャッシュを使用するためのロジック)と(キャッシュされたデータを格納するための)追加のメモリが必要です。

ここで引用しているように、それは「キャッシング」です。

コンテキストから、私はそれの感覚をつかむことができます。多くの場合、取得した値をメインメモリに保存し、クイックルックアップアクセスします。


もう1つはCPUキャッシングで、これは このWikipediaの記事 で説明されています。 CPUキャッシングは自動的に行われます。少量のメモリから大量の読み取りを行う場合、CPUはキャッシュからこれらの読み取りのほとんどを実行できます。 OTOHは、大量のメモリから読み取る場合、すべてがキャッシュに収まらないため、CPUはより低速なメモリの処理により多くの時間を費やす必要があります。

ここで引用しているように、それは「キャッシング」です。

キャッシュを傷つけるコードを見つけたと誰かが言うと、それを修正した後、アプリの速度が向上しました。

つまり、コードを再配置して キャッシュミス を減らす方法を見つけたということです。


データベースキャッシングについては、わかりません。

5
ChrisW

いくつかの問題があります。

1つは、粒度です。アプリケーションは、データベースが実行する以上の非常に細かいレベルのキャッシュを持つことができます。たとえば、データベースは単に特定の行ではなく、単にデータのページをキャッシュする可能性があります。

もう1つのことは、アプリケーションがデータを「ネイティブ」形式で保存できるのに対して、DBは明らかに内部形式でのみキャッシュすることです。

簡単な例。

データベースにUSERIDFIRSTNAMELASTNAMEの列で構成されるユーザーがいるとします。とても簡単です。

ユーザー_USERID=123_をアプリケーションにロードします。関係する手順は何ですか?

  1. データベース呼び出しの発行
  2. リクエストの解析(_SELECT * FROM USER WHERE USERID = ?_)
  3. リクエストの計画(つまり、システムがデータをフェッチする方法)
  4. ディスクからデータを取得する
  5. データベースからアプリケーションへのデータのストリーミング
  6. データベースデータをアプリケーションデータに変換します(つまり、USERIDを整数に、たとえば名前を文字列に変換します。

データベースキャッシュは、おそらくステップ2と3をキャッシュし(ステートメントキャッシュであるため、クエリを解析または再計画しません)、実際のディスクブロックをキャッシュします。

だから、ここに鍵があります。ユーザー、_USER ID 123_、名前_JESSE JAMES_。これは大量のデータではないことがわかります。ただし、データベースはディスクブロックをキャッシュしています。インデックスブロック(_123_が付いている)、データブロック(実際のデータ、およびそのブロックに収まる他のすべての行)があります。つまり、名目上、たとえば60〜70バイトのデータは、実際には、おそらく4K〜16K(ブロックサイズによって異なります)のキャッシュとデータへの影響をDBに与えます。

明るい面?近くにある別の行(_USER ID = 124_など)が必要な場合、インデックスとデータが既にキャッシュされている可能性が高くなります。

ただし、そのキャッシングを使用しても、データをネットワーク経由で移動するためのコストを支払う必要があり(ローカルDBを使用している場合を除き、ネットワーク経由で常にループバックします)、データを「非整列化」します。 。つまり、データベースビットから言語ビット、アプリケーションビットに変換します。

これで、アプリケーションが_USER ID 123_を取得すると、長期間有効なハッシュマップに値を詰め込みます。

アプリケーションが再びそれを必要とする場合、ローカルマップ、アプリケーションキャッシュを検索し、ルックアップ、ワイヤトランスポート、およびマーシャリングのコストを保存します。

アプリケーションのキャッシングのダークサイドは同期です。誰かが入って_UPDATE USER SET LASTNAME="SMITH" WHERE USERID=123_を実行した場合、アプリケーションは「それを認識していない」ため、キャッシュが汚れています。

そのため、その関係を処理して、アプリケーションとDBの同期を維持するための詳細がたくさんあります。

大量のデータベースキャッシュがあることは、「ホット」なデータセットに対する大規模なクエリにとって非常に便利です。メモリが多いほど、「ホット」なデータを多く持つことができます。 RAMにDB全体をキャッシュできる場合は、ディスクからデータをRAMバッファーに移動するI/O(少なくとも読み取りの場合)の遅延を排除します。輸送とマーシャリングの費用がかかります。

アプリケーションは、より限定的なデータのサブセット(DBはキャッシュブロックのみ)をキャッシュするなど、はるかに選択的である可能性があり、アプリケーションにより近い「データ」を持つことにより、パフォーマンスが大幅に向上します。

欠点は、すべてがアプリケーションにキャッシュされるわけではないことです。データベースは、アプリケーションよりも全体的に効率的にデータを保存する傾向があります。また、アプリのキャッシュデータに対する「クエリ」言語もありません。ほとんどの人は単純なキーを介して単にキャッシュし、そこから移動します。 「ALL USERS NAMED JESSE」の場合は、_USER ID 123_を簡単に見つけられます。

データベースのキャッシュは「無料」になる傾向があり、バッファ番号を設定すると、DBMSが残りを処理します。影響が少なく、全体的なI/Oとディスクの遅延が減少します。

アプリケーションキャッシングは、アプリケーション固有です。

孤立した「静的」データに対して非常にうまく機能します。とても簡単です。起動時にルックアップテーブルに大量のものを読み込み、変更された場合はアプリを再起動します。それは簡単です。

その後、「ダーティ」ロジックなどを追加すると複雑さが増し始めます。

結局のところ、データAPIがあれば、増分的にキャッシュできるということです。

したがって、DBにアクセスするのではなく、どこでもgetUser(123)を呼び出す限り、コードに影響を与えることなく後で戻ってgetUserにキャッシュを追加できます。

そのため、私は常に、すべてのコードで何らかの種類のデータアクセス層を提案し、そのビットの抽象化層と傍受層を提供します。

4
Will Hartung

キャッシングは長いアルゴリズムまたはCPUを集中的に使用するアルゴリズムの結果を取得し、アルゴリズムを再度実行する必要がないように答えを保存します。結果を再利用するだけです。

2
Gregor Brandt

キャッシュの概念はここではオーバーロードされた用語です。私は、データベースキャッシングの要点に精通していません。

アプリケーションでは、この用語には2つの用途があります。

キャッシュを傷つけるコードを見つけたと誰かが言うと、それを修正した後、アプリの速度が向上しました。

この場合、CPUキャッシュを参照しています。

CPUキャッシュはCPU上のメモリであり、RAMよりもはるかに高速ですが、ランダムアクセスはありません。 CPUがキャッシュにロードすることを決定するものは、少し複雑になる可能性があります。詳細については、Ulrich Dreppers すべてのプログラマーがメモリについて知っておくべきこと を参照してください。

CPUキャッシュに注意することで、物事をかなり高速化できます-物事が物理メモリ内で相互に関連して配置される場所と、それらが使用される可能性が高い場合に注意する必要があります。

1つの例(おそらく保守性のアンチパターン)は、構造体の配列があり、フィールドがすべて配列である構造体を使用する方が適切な構造体のメンバーで多くのループを実行することです。ループオーバーしているデータがメモリ内で連続している場合、キャッシュを混乱させない可能性が高くなります。

あらゆる種類のものがキャッシュの使用効率に影響を与える可能性があります-キャッシュにロードされるコードの分岐予測、データ構造とアクセスパターンのサイズとアライメント、スタックに配置されるローカル変数を宣言する場所とタイミング。

アプリケーションプログラミングのこの用語の他の一般的な使用法は、 memoization と呼ばれるものによって行うことができます。そのウィキペディアのページにある要因の例は、私がやったよりも良いことを説明しています。

2
Dave

通常、データベースのキャッシュはデータベースの機能であり、データベースによって自動的に管理されます。アプリケーションのキャッシュは、プラットフォームごとに異なります。

オブジェクトキャッシュは、よく使用されるオブジェクトをメモリに格納するために使用できるメカニズムであり、データを取得して再作成するためにコストを支払う必要がありません。これは一般にコードで管理され、使用しているキャッシングソリューションによって異なります。

複数のサーバーでサービスを設定して、ある種のキャッシュファームを提供する分散キャッシュソリューションがあります。これにより、スケーラビリティと冗長性が提供されます。クライアントは、ネットワーク全体でキャッシュされた情報を要求できます。繰り返しますが、これはコード内の手動の手順です。分散キャッシュプロバイダーの例はmemcachedです。

http://www.danga.com/memcached/

特定のタイプのキャッシングの例は、asp.netキャッシングです。 Asp.netは、いくつかの種類のキャッシュをサポートしています。従来のオブジェクトキャッシュがあります(Webサイトだけでなく、あらゆる種類の.netアプリで使用できます)。ページとユーザーコントロールを構成して出力を自動的にキャッシュできるようにするキャッシュ機能もあります。これはデータをキャッシュせず、最終結果(ページのHTML)をキャッシュし、ユーザーが前のユーザーと同じクエリ文字列パラメーターを使用して同じページを要求したときにそれを提供します。

1
Jim Petkus

それはおそらくあなたが想像するよりも簡単だと思います-そしてそれは人々がそれを閉じようとしている理由です。

毎回データベースに戻るのではなく、メモリに値を保存するだけです。

これを行うには多くの方法がありますが、概念自体は簡単です。

編集:どのレベルでも実行できます。長時間かかるものはどこでもキャッシュでき、より速くアクセスできます。

0
Bill K

キャッシングは必ずしも「取得済みの」値に適用されるだけでなく、再計算の回数を減らすことで時間を節約できるものに適用されます。頭に浮かぶ簡単な例は、 フィボナッチ数列 の計算です。最も単純な再帰的実装は、次のようになります(擬似コード):

function f(n)
    if n < 2 then
        return n;
    return f(n - 1) + f(n - 2)

これは、既知の値の再計算を防ぐためにキャッシングで改善できます。

fib_cache = {}

function f(n)
    if n < 2 then
        return n;
    if fib_cache.contains(n) then
        return fib_cache[n]
    fib_cache[n] = f(n - 1) + f(n - 2)
    return fib_cache[n]
0
Kevin Loney