web-dev-qa-db-ja.com

MySQLとInnoDBのキャッシュは、同じではないが2つの類似した選択リクエストを実行すると、内部でどのように機能しますか?

Innodbバッファープールがどのように機能するかについて、いくつか混乱があります。

15レコードのテーブルがあるとしましょう。もしselect ... between 1 to 10を実行すると、その結果はbuffer(?)にキャッシュされます。私の質問は、次回select .... between 1 to 15を実行すると、レコードがどのようにフェッチされるかです。バッファから10レコード、ディスクから5レコードを選択していますか?

5
Sudeep shetty

あなたの質問、「MySQL選択リクエストが内部でどのように機能するか」は非常に幅広いトピックです。ただし、多少正確ではありませんが、キャッシュに関して「レコード1から10を選択し、次にレコードを1から15に選択するとどうなるか」の簡単な概要を説明します。

あなたが最初に理解しなければならないことは、機能的に、MySQLは2つの別個のレイヤー、「フロントエンド」またはSQLレイヤー、およびENGINEレイヤーに分割できるということです。物事を簡素化しています) 行ベースのapi 。 InnoDBまたは他のプラグイン可能なエンジンで行を格納することができます(その1つだけに注目しましょう)。キャッシュはSQLまたはエンジンレベルで発生する可能性があります。

SQLレイヤー

SQLレイヤーにはいくつかのキャッシュメカニズムがあります。クエリ結果のキャッシュに焦点を当てましょう。これはQuery Cacheとして知られています。エンジンにアクセスせずにクエリ結果を返すために、メモリに一定量のスペースを割り当てることができます。これは非常に高レベルのキャッシュ方法であり、保存されている実際のデータと一致している必要があり、その実装は非常に単純化されているため、多くの実際的な問題が発生します。

  • 書き込みのグローバルロックが必要です。これにより、書き込み数が少ない場合でも、すべての並列処理が強制終了され、中〜高負荷のもとで高い競合が発生します。
  • ミスを遅らせることができます
  • エビクションも有害である可能性があり、結果が影響を受けない場合でも、テーブルが書き込まれるとすぐにすべての結果が無効になります
  • 同じ結果が返されてもクエリが少し異なる場合、それらは2つの異なるクエリと見なされ、同じクエリを2回格納します
  • パーティション分割されたテーブルはサポートされておらず、Galeraなどのクラスタリングソリューションはサポートしていません
  • すべてのクエリがキャッシュ可能であるわけではなく(たとえば、不確定なクエリ)、それらには有限数のスペースがあります。

あなたの場合、2つの異なるクエリを実行した場合、クエリキャッシュは役に立たず、2つの失敗があり、その後に結果の2つの個別の挿入が続きます(キャッシュ可能であると仮定した場合など)。

多くの人はクエリキャッシュを無効にし、他のバッファに追加のメモリを使用することを推奨します。正確な影響は、特定のワークロードによって異なります(ヒット/ミス/エビクションとクエリの待機時間を監視する必要があります)。それでもクエリキャッシュが必要な場合は、アプリケーションレイヤーに近いmemcacheなどを選択できます。 MySQLでさえ、サーバーの一部として memcacheをInnoDBのフロントエンドとして実行するプラグイン を統合しています。

キャッシュを分離すると、キャッシュされた結果のTTLを自分で制御できます。 ProxySQL など、それを処理できるメカニズムは他にもたくさんあります。応用。

InnoDB

InnoDBは、独自の複雑なキャッシュシステムを実装して、行の取得を高速化しますバッファプール内(そのページキャッシュ)、はるかに低レベルのキャッシュ。一般に、InnoDBはファイルシステムキャッシュを回避(回避)し、独自の行キャッシュシステムを実装しようとします。デフォルトのページサイズは16KBです(変更可能です)。ディスク上でもメモリ内と同じ構造です。ページは、2行以上のデータを格納するのに十分な大きさである必要があります(行は、可変サイズのフィールドの場合、8Kより大きい場合がありますが、これは別の話です)。ディスク上のページにアクセスすると、そのページはバッファプールにコピーされます。実際、いくつかの最適化により、多くの場合、単一のページが読み取られるだけでなく、[複数の連続するページを削減するためにIO operations]になります。

したがって、空のバッファプールで最初の10行を読み取ると、必要な行とおそらくそれ以上のページだけがメモリに読み込まれます。そして多くの場合、完全な拡張と次の拡張。これはすべて調整の対象であり、使用しているアクセスパターンの種類によって異なります。

もう一度読むと、おそらくメモリ内に同じ行が見つかるため、ディスクI/Oが減少しています。一部のページが欠落している場合は、1つ(または複数)のディスクIO操作が必要です。これは通常、同じクエリを2回実行してより高速な応答を取得したときに発生します。行のキャッシュは高速化の一部-スレッドキャッシュ、テーブルキャッシュ、ディクショナリキャッシュ、インデックスキャッシュ...(SQLレベルとエンジンレベルの両方で)もあります。

ただし、2番目のクエリを実行するまでに、行が強制排除されている場合があります。強制排除は比較的複雑なアルゴリズムに従い、高度に調整可能ですが、LRUを簡略化しています( 最近使用されていない )実装。これは、最後にアクセスされた他のデータがメモリに入ると、ほとんど使用されないデータを削除できることを意味します。

ここでは、InnoDBバッファープールがどのように機能するかを段階的に要約しています。

他のエンジンは異なる方法で処理を実行できます。たとえば、MyISAMとTokuDBは両方ともインデックスキャッシュを持っていますが、どちらもデータのファイルシステムキャッシュをより多く中継します。

6
jynus

短い答え...両方のクエリで1つのディスクのみを読み取ります。 (15ではなく、2ではありません。)

中程度の回答...

おそらく、単純な例では、最初にテーブルを「開く」ためにいくつかのディスクヒットがあり、次に最初の「ブロック」を読み取るために1つのディスクヒットがありました。

InnoDBはbuffer_poolを通じてすべてのデータとインデックスをキャッシュします。行1..10をフェッチしたとき、それらはすべて1つのブロックにある場合とそうでない場合があり、1ディスクヒットを必要とします。そして、そのブロックには11..15が含まれている場合と含まれていない場合があります。

単純な経験則では、1つのブロックをフェッチすると100行がフェッチされます。ブロックは常に16KBなので、実際の行数は行のサイズ、および(月の満ち欠けのように見える)によって異なります。

0
Rick James