ユニークなインデックス検索が速いと聞いたことがありますか?


インデックスを作成するとき、フルテキストインデックス、主キーインデックス、一意のインデックス、共通インデックスなどがあります。最初の2つは理解しやすく、区別しやすいです。誰もがそれをいつ使用するか、そして後者の2つを区別する方法を知っていますか?ユニークインデックスと通常インデックスの選び方は?今日はこのトピックについて話します。

1.準備

次の表があるとします。

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `age` int(4) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  KEY `address` (`address`)
) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

この表には10万個のシミュレーションデータがありますが、自分で10万個のシミュレーションデータを作成しても大したことはありません。

テーブル構造を見ると、一意のインデックスであるユーザー名インデックスと、共通のインデックスであるアドレスインデックスがあります。

2.お問い合わせ

2.1一般的なインデックスクエリ

まず、通常のインデックスのクエリを見てみましょう。

簡単なクエリを実行してみましょう。

select * from user where address='1';

以前の説明(インデックスプッシュダウン、yyds!)によると、ここでクエリ手順を整理しましょう。

  1. MySQLのサーバー層は、最初にストレージエンジンを呼び出して、値が1の最初のアドレスを見つけます。
  2. アドレスはセカンダリインデックスであり、プライマリキー値はセカンダリインデックスのリーフノードに格納されているため、実際にはリターンテーブルであるプライマリキー値に従って、プライマリキーインデックスの完全なデータ行を見つける必要があります。 (MySQLの「リターンテーブル」とは何ですか?)。
  3. ストレージエンジンは、読み取ったデータ行をサーバーレイヤーに返します。
  4. アドレスは一意のインデックスではなく共通のインデックスであるため、アドレスが1のレコードが複数存在する可能性があります。したがって、最初のクエリに基づいて、リーフノード内の単一リンクリストに沿って逆方向にスキャンし、スキャンします。新しいデータの後に、手順2と3を繰り返します。
  5. アドレスが1でないレコードにスキャンする場合は、スキャンを停止してください。

上記は私たちの分析です、実行計画を見てみましょう:

実行プランのタイプはrefです。これは、分析が正常であることを意味します。

2.2一意のインデックスクエリ

一意のインデックスクエリをもう一度見てみましょう。

最初にSQLを見てみましょう。

select * from user where username='1';

一意のインデックスの場合、username列の値は一意であるため、クエリプロセスでusername='1'は、最初のレコードを見つけた再度検索する必要はありません。通常のインデックスのクエリステップと比較すると、ステップ4が少ないことに相当します。および5。

クエリプランを見てみましょう。

以前の通常のインデックスのクエリプランと比較すると、ここでのクエリプランタイプはconstであり、これもステートメントを確認します。

2.3 PK

したがって、上記の説明から、クエリを実行すると一意のインデックスのパフォーマンスが向上するように見えることがわかりますか?状況はどうですか、それを分析しましょう。

まず、理論的には、クエリを実行する場合は一意のインデックスの方が優れています。理由は非常に単純です。一意のインデックスが条件を満たすレコードを見つけた後、再度検索する必要はありません。通常のインデックスが次のレコードを見つけた後です。条件を満たしている場合は、逆方向に検索を続行し、条件を満たさないレコード(アドレスが1でないレコード)に遭遇するまで検索を停止する必要があるため、一意のインデックスの方が確かに優れているようです。では、違いは目立ちますか?正直なところ、この利点はごくわずかです。

なぜ?

  1. 通常のインデックスでは、最初のレコードが見つかりましたが、次のレコードを探し続ける必要がありますが、条件を満たすレコードは連続しているため、インデックスは、レコード間の単一リンクリストに沿って逆方向に読み続けるだけで済みます。 。高速。
  2. InnoDBエンジンがデータを読み取るとき、データは1つずつ読み取られるのではなく、1ページずつ読み取られるためです(デフォルトは1ページあたり16KBです。MySQLの「リターンテーブル」とは何ですか?この記事では、 16KBの概要を説明します。問題)、したがって、逆方向に読み続けても、メモリ操作であり、非常に高速です。
  3. 個々のケースは除外されません。たとえば、条件を満たすレコードはたまたま現在のページの最後のレコードであり、この時点で新しいページのデータをロードする必要がありますが、この確率は比較的小さく、無視できます。 。

要約すると、検索効率に対する一意のインデックスと一般的なインデックスの影響はごくわずかです。

3挿入/変更

3.1知識の準備

3.1.1バッファプール

誰もが知っておく必要のあるバッファプールがあります。

友人は、InnoDBエンジンがデータを格納するとき、それがページ単位であることを知っています。各データページのデフォルトサイズは16KBです。次のコマンドでページのサイズを確認できます。

16384/1024=16

正確に16KB。

コンピューターがデータを格納する場合、最小ストレージ単位はセクターであり、セクターのサイズは512バイトですが、ファイルシステム(XFS / EXT4など)の最小単位はブロックであり、ブロックのサイズは4ブロックである4KB。InnoDBでページを形成します。MySQLでは、データベースの追加、削除、変更、チェックの操作はすべてデータページを操作しています。率直に言って、ディスクを操作しています。

しかし、考えてみてください。毎回ディスクを操作すると、大量のディスクIO操作が発生します。従来のメカニカルハードディスクの場合は、ランダムなIO操作も多く、効率が非常に高くなります。これは、MySQLのパフォーマンスに深刻な影響を及ぼします。

この問題を解決するために、MySQLはバッファプールを導入しました。これは、私たちがしばしばバッファプールと呼ぶものです。

バッファプールの主な機能は、インデックスとテーブルデータをキャッシュして、各操作のディスクIOを回避することです。データアクセス速度は、バッファプールを介して向上させることができます。

次のコマンドを使用して、バッファプールのデフォルトサイズを表示できます。

134217728/1024/1024=128

ここでのMySQLはDockerにインストールされているため、デフォルトのサイズは128MBです。したがって、この割り当ては小さくなります。一般的に、サーバーがMySQLサービスのみを実行している場合、バッファープールサイズをサーバーメモリサイズの75%から80%に設定できます。

3.1.2バッファの変更

誰もが知っておく必要のある変更バッファーもあります。

前述のバッファプールはアクセス速度を向上させましたが、追加、削除、変更の効率は向上していません。追加、削除、変更に関しては、ディスクIOが必要であるため、効率はそれと同じくらい低くなります。ぞっとする。

この問題を解決するために、MySQLに変更バッファーが導入されました。変更バッファーは、以前はこの名前と呼ばれていませんでした。以前は挿入バッファーと呼ばれていましたが、これは挿入操作にのみ有効です。現在は、変更バッファーに名前が変更されています。これは、挿入だけでなく、削除および更新操作にも有効です。変更バッファーは主に非一意の操作用です。インデックスは有効です。フィールドが一意のインデックスである場合、更新時に一意性を確認する必要があり、ディスクIOは回避できません。

バッファの変更とは、データベース内のデータを変更する必要がある場合、メモリ内の変更を記録し、将来データが読み取られるときに、メモリ内のデータをバッファプールにマージすることを意味します。データとディスク内のデータ。異なるデータをダーティページと呼びます。条件が満たされると(REDOログがいっぱい、メモリがいっぱい、その他のアイドル時間)、InnoDBはダーティページをフラッシュしてディスクに戻します。 。この方法は、書き込み操作のディスクIOを効果的に削減し、データベースのパフォーマンスを向上させることができます。

次のコマンドを使用して、変更バッファーのサイズと変更バッファーに関連する操作を確認できます。

  • innodb_change_buffer_max_size:この構成は、バッファー・プール全体に対する変更バッファー・サイズの比率を示します。デフォルト値は、、25%最大値は50%です。
  • innodb_change_buffering:この操作は、どの書き込み操作が変更バッファーを使用するかを示します。デフォルトのallは、すべての書き込み操作を示します。自分で////などに設定するnoneことinsertsできますdeleteschangespurges

ただし、変更バッファとバッファプールはどちらもメモリ操作を伴うため、データを永続化できません。ダーティページがある場合、MySQLが突然ハングすると、データが失われる可能性があります(メモリ内のデータが書き込まれていないため)。ディスク。)しかし、実際にMySQLを使用する場合、実際にはこの問題は発生しません。問題はどのように解決されますか?次に、REDOログに頼る必要があります。このSonggeは、記事を書き、将来、REDOログをすべての人に紹介します。

3.2 PK

上記の変更バッファの概要を読んだ後、あなたはすでに理解しているはずです:

  • 一意でないインデックスの場合、挿入時に変更バッファーにデータを直接格納するだけで十分です。これはメモリ操作であり、非常に高速です。
  • 一意のインデックスの場合、挿入するときに、データページをメモリに読み込む必要があり(この手順には多くのランダムIOが含まれるため、非効率的です)、競合がないことを確認してから挿入します。

したがって、明らかに、挿入するときは、一意でないインデックスの方が有利です。

4.まとめ

では、グローバルに一意である必要があるフィールドの場合、共通のインデックスを使用する必要がありますか、それとも一意のインデックスを使用する必要がありますか?データベースの最適化は必ずしも絶対的なものではなく、実際のビジネスと組み合わせてデータベースの最適化について議論する必要があるため、万能の提案をすることは難しいと思います

フィールドがビジネスから一意であることを確認できる場合は、通常のインデックスを使用できます。これにより、挿入/更新の速度を向上させることができます。

ただし、マーフィーの法則によれば、一意のインデックスを使用しない場合、将来的にフィールドでダーティ値が発生する可能性が高くなるため、ダーティ値のビジネス許容度も考慮する必要があります。

おすすめ

転載: blog.csdn.net/u012702547/article/details/122684863