MySQLの理解-インデックスと最適化

前書き:インデックスはクエリの速度に決定的な影響を与えます。インデックスを理解することは、データベースのパフォーマンスチューニングの開始点でもあります。次の状況を考えてみます。データベース内のテーブルに10 ^ 6レコードがあり、DBMSのページサイズが4Kで、100レコードが格納されているとします。インデックスがない場合、クエリはテーブル全体をスキャンします。最悪の場合、すべてのデータページがメモリにない場合は、10 ^ 4ページを読み取る必要があります。これらの10 ^ 4ページがディスク上にランダムに分散されている場合、 10 ^ 4 I / O、ディスクI / O時間が毎回10msであると仮定すると(データ送信時間を無視)、合計で100秒かかります(実際にははるかに優れています)。そのためのBツリーインデックスを作成する場合は、log100(10 ^ 6)= 3ページの読み取りを実行するだけで済み、最悪の場合は30ミリ秒かかります。これはインデックスによってもたらされる効果です。多くの場合、アプリケーションでSQLクエリの実行が遅い場合は、インデックスを作成できるかどうかを検討する必要があります。タイトルに:

第1章インデックス作成と最適化

MySQLの理解-インデックス作成と最適化のビデオ説明:https //www.bilibili.com/video/BV1uz4y1671b/

1.インデックスのデータ型を選択します

MySQLは多くのデータ型をサポートしており、データを格納するための適切なデータ型を選択すると、パフォーマンスに大きな影響を与えます。一般的に、次のガイドラインに従うことができます。

(1)通常、データ型が小さいほど優れています。データ型が小さいほど、ディスク、メモリ、およびCPUキャッシュに必要なスペースが少なくて済み、処理が高速になります。

(2)単純なデータ型の方が優れています。文字列の比較がより複雑であるため、整数データは文字よりも処理のオーバーヘッドが少なくなります。MySQLでは、文字列の代わりに組み込みの日付と時刻のデータ型を使用して時刻を格納し、整数のデータ型を使用してIPアドレスを格納する必要があります。

(3)NULLを回避するようにしてください。NULLを格納する場合を除いて、列をNOTNULLとして指定する必要があります。MySQLでは、null値を持つ列は、インデックス、インデックス統計、および比較操作をより複雑にするため、最適化をクエリするのが困難です。null値を0、特別な値、または空の文字列に置き換える必要があります。

1.1、識別子を選択

適切な識別子を選択することは非常に重要です。選択するときは、ストレージタイプだけでなく、MySQLが操作と比較を実行する方法も考慮する必要があります。データ型を選択したら、関連するすべてのテーブルが同じデータ型を使用していることを確認する必要があります。

(1)整数:処理が速く、AUTO_INCREMENTに設定できるため、通常は識別子として最適です。

(2)文字列:文字列を識別子として使用しないようにしてください。文字列はより多くのスペースを消費し、処理が遅くなります。さらに、一般的に言えば、文字列はランダムであるため、インデックス内の位置もランダムであり、ページ分割、ディスクへのランダムアクセス、およびクラスター化インデックス分割(クラスター化インデックスを使用するストレージエンジンの場合)が発生します。

2.インデックス作成の開始

どのDBMSでも、インデックスは最適化の最も重要な要素です。少量のデータの場合、適切なインデックスがないことによる影響は大きくありませんが、データの量が増えると、パフォーマンスが大幅に低下します。

複数の列(複合インデックス)にインデックスを付ける場合、列の順序は非常に重要であり、MySQLはインデックスの左端のプレフィックスに対してのみ効果的な検索を実行できます。例えば:

複合インデックスit1c1c2(c1、c2)があるとすると、クエリステートメントselect * from t1 where c1 = 1 and c2 = 2 can use thisindex。クエリステートメントselect * from t1ここで、c1 = 1もこのインデックスを使用できます。ただし、クエリステートメントselect * from t1 where c2 = 2は、複合インデックスの先頭の列がないため、インデックスを使用できません。つまり、検索にc2列を使用する場合、c1は特定の値と等しくなければなりません。 。

C / C ++ Linuxサーバー開発のよりエキサイティングなコンテンツには、C / C ++、Linux、Nginx、ZeroMQ、MySQL、Redis、MongoDB、ZK、ストリーミングメディア、P2P、Linuxカーネル、Docker、TCP / IP、coroutine、DPDK共有が含まれます。複数の高度な知識ポイントの。リンクをクリックしてサブスクライブし、直接視聴してくださいhttps //ke.qq.com/course/417774?flowToken = 1013189

2.1、インデックスのタイプ

インデックスは、サーバーレイヤーではなく、ストレージエンジンに実装されます。したがって、各ストレージエンジンのインデックスは必ずしも同じではなく、すべてのストレージエンジンがすべてのインデックスタイプをサポートしているわけではありません。

2.1.1、Bツリーインデックス

次のようなテーブルがあるとします。

インデックスには、テーブルの各行のlast_name、first_name、およびdob列が含まれます。その構造はおおまかに次のとおりです。

インデックスに格納されている値は、インデックス列の順序で配置されます。Bツリーインデックスを使用して、完全なキーワード、キーワード範囲、およびキーワードプレフィックスをクエリできます。もちろん、インデックスを使用する場合は、インデックスの左端のプレフィックスでクエリを実行する必要があります。

(1)完全な値に一致する:インデックス内のすべての列に特定の値を割り当てます。たとえば、上の画像のインデックスは、1960-01-01に生まれたキューバアレンを見つけるのに役立ちます。

(2)左端のプレフィックスに一致する:インデックスを使用して、インデックスの最初の列のみを使用して、姓がAllenである人を見つけることができます。

(3)列プレフィックスの一致:たとえば、インデックスを使用して、姓がJで始まる人を検索できます。これはインデックスの最初の列のみを使用します。

(4)値の範囲を一致させる:インデックスを使用して、インデックスの最初の列のみを使用して、姓がAllenとBarrymoreの間にある人を見つけることができます。

(5)ある部分を正確に一致させ、別の部分の範囲を一致させる(ある部分を正確に一致させ、別の部分の範囲を一致させる):インデックスを使用して、姓がAllenで、名が文字Kで始まる人を見つけることができます。 。

(6)インデックスのみのクエリ:クエリ列がすべてインデックスにある場合、タプルの値を読み取る必要はありません。

Bツリー内のノードは順番に格納されるため、インデックスを使用して検索(特定の値を検索)でき、クエリ結果をORDERBYにすることもできます。もちろん、Bツリーインデックスの使用には次の制限があります。

(1)クエリは、インデックスの左端の列から開始する必要があります。この点は何度も言及されています。たとえば、インデックスを使用して特定の日に生まれた人を見つけることはできません。

(2)インデックス列はスキップできません。たとえば、インデックスを使用して、姓がスミスで、特定の日に生まれた人を見つけることはできません。

(3)ストレージエンジンは、インデックスの範囲条件の右側の列を使用できません。たとえば、クエリがWHERE last_name = "Smith" AND first_name LIKE'J% 'AND dob =' 1976-12-23 'の場合、LIKEは範囲クエリであるため、クエリはインデックスの最初の2列のみを使用します。 。

2.1.2、ハッシュインデックス

MySQLでは、メモリストレージエンジンのみが、メモリテーブルのデフォルトのインデックスタイプであるハッシュインデックスをサポートしていることを示していますが、メモリテーブルはBツリーインデックスも使用できます。メモリストレージエンジンは、データベースフィールドではまれな一意でないハッシュインデックスをサポートします。複数の値が同じハッシュコードを持っている場合、インデックスはそれらの行ポインタをリンクリストの同じハッシュエントリに保存します。

次のテーブルを作成するとします。

含まれるデータは次のとおりです。

次のように、インデックスがハッシュ関数f()を使用するとします。

現時点でのインデックスの構成は、おおまかに次のとおりです。

スロットは順番に並んでいますが、レコードは順番に並んでいません。

mysql> SELECT lname FROM testhash WHERE fname = 'Peter';を実行すると、

MySQLは「Peter」のハッシュ値を計算し、それを使用してインデックス付き行ポインタをクエリします。f( 'Peter')= 8784であるため、MySQLはインデックスで8784を検索し、レコード3へのポインタを取得します。

インデックス自体は短い値しか格納しないため、インデックスは非常にコンパクトです。ハッシュ値は列のデータ型に依存しません。TINYINT列のインデックスは、長い文字列列のインデックスと同じ大きさです。

ハッシュインデックスには次の制限があります。

(1)インデックスにはハッシュコードとレコードポインタしか含まれていないため、MySQLはインデックスを使用してレコードを読み取ることを避けられません。しかし、メモリ内のレコードへのアクセスは非常に高速であり、性別にはあまり影響しません。

(2)ハッシュインデックスを使用してソートすることはできません。

(3)ハッシュ値はインデックス値全体から計算されるため、ハッシュインデックスはキーの部分一致をサポートしていません。

(4)ハッシュインデックスは、=、IN()、<=>の使用など、同等性の比較のみをサポートします。WHERE price> 100の場合、クエリは高速化されません。

2.1.3空間(Rツリー)インデックスMyISAMは、主にGEOMETRYなどの地理空間データタイプに使用される空間インデックスをサポートします。

2.1.4、フルテキストインデックス

フルテキストインデックスは、MyISAMの特殊なインデックスタイプであり、主にフルテキスト検索に使用されます。

3.高性能インデックス戦略

3.1、クラスター化インデックス(クラスター化インデックス)

クラスター化インデックスは、同様のキー値を持つタプルの物理的な場所も同じであることを保証します(したがって、文字列タイプはクラスター化インデックス、特にランダムな文字列を確立するのに適していないため、システムは多数のモバイルを実行します操作)、およびテーブルはクラスター化インデックスのみが可能です。インデックスはストレージエンジンによって実装されるため、すべてのエンジンがクラスター化インデックスをサポートしているわけではありません。現在、solidDBとInnoDBのみがサポートされています。クラスター化インデックスの構造は、おおまかに次のとおりです。

注:リーフページには完全なタプルが含まれますが、内部ノードページにはインデックス付きの列のみが含まれます(インデックス付きの列は整数です)。一部のDBMSでは、ユーザーがクラスター化インデックスを指定できますが、MySQLのストレージエンジンはこれまでサポートしていません。InnoDBは、主キーにクラスター化インデックスを作成します。主キーを指定しない場合、InnoDBは代わりに一意のnull以外の値を持つインデックスを使用します。そのようなインデックスがない場合、InnoDBは非表示の主キーを定義し、その上にクラスター化インデックスを構築します。一般的に、DBMSは、他のセカンダリインデックスの基礎となるクラスター化インデックスの形式で実際のデータを格納します。

3.1.1.InnoDBとMyISAMのデータレイアウトの比較

クラスター化インデックスと非クラスター化インデックス、またはプライマリインデックスとセカンドインデックス(MyISAMはクラスター化インデックスをサポートしていません)をよりよく理解するために、InnoDBとMyISAMのデータレイアウトを比較してみましょう。

主キーの値が1〜10,000であり、ランダムな順序で挿入されていると仮定して、OPTIMIZETABLEを使用して最適化します。col2には1から100までの値がランダムに割り当てられるため、重複する値が多数あります。

(1)MyISAMのデータレイアウト

レイアウトは非常にシンプルで、MyISAMは次のように挿入順にデータをディスクに保存します。

注:左側は0から始まる行番号です。タプルのサイズは固定されているため、MyISAMはテーブルの先頭から特定のバイトの位置を簡単に見つけることができます。

確立された主キーインデックス構造によると、おおよそ次のとおりです。

注:MyISAMはクラスター化インデックスをサポートしていません。インデックス内の各リーフノードには行番号のみが含まれ、リーフノードはcol1の順序で格納されます。

col2のインデックス構造を見てください。

実際、MyISAMでは、主キーは他のインデックスと同じです。主キーは、PRIMARYと呼ばれる空でない一意のインデックスです。

(2)InnoDBデータレイアウト

InnoDBはデータをクラスター化インデックスの形式で格納するため、データのレイアウトは大きく異なります。ストレージテーブルの構造はおおまかに次のとおりです。

注:クラスター化インデックスの各リーフノードには、主キーの値、トランザクションID、ロールバックポインター(ロールバックポインター)(トランザクションとMVCC用)、および残りの列(col2など)が含まれます。

MyISAMと比較すると、セカンダリインデックスはクラスター化インデックスとは大きく異なります。InnoDBのセカンダリインデックスのリーフには、行ポインターの代わりにプライマリキーの値が含まれます。これにより、InnoDBはインデックスの行ポインターを更新する必要がないため、データの移動時またはデータページの分割時にセカンダリインデックスを維持するオーバーヘッドが削減されます。その構造はおおまかに次のとおりです。

クラスター化インデックスと非クラスター化インデックステーブルの比較:

3.1.2、主キーの順序で行を挿入します(InnoDB)

InnoDBを使用していて、特別なクラスター化インデックスが必要ない場合は、アプリケーションのデータに関係なく、代理キーを使用することをお勧めします。最も簡単な方法は、AUTO_INCREMENT列を使用することです。これにより、レコードが順番に挿入され、主キーを使用して接続するクエリのパフォーマンスを向上させることができます。主キーのランダムなクラスタリングを回避しようとする必要があります。たとえば、文字列の主キーは不適切な選択であり、挿入操作がランダムになります。

3.2、カバーインデックス

インデックスにクエリを満たすすべてのデータが含まれている場合、それはカバーリングインデックスと呼ばれます。カバーインデックスは、クエリのパフォーマンスを大幅に向上させることができる非常に強力なツールです。データを読み取らずにインデックスを読み取るだけでよいという利点は次のとおりです。

(1)インデックス項目は通常レコードよりも小さいため、MySQLがアクセスするデータは少なくなります。

(2)インデックスは値順に格納されるため、ランダムアクセスレコードと比較して必要なI / Oが少なくなります。

(3)ほとんどのデータエンジンは、インデックスをより適切にキャッシュできます。たとえば、MyISAMはインデックスのみをキャッシュします。

(4)InnoDBはクラスター化インデックスを使用してデータを編成するため、インデックスのカバーはInnoDBテーブルで特に役立ちます。セカンダリインデックスにクエリに必要なデータが含まれている場合、クラスター化インデックスを検索する必要はありません。

カバーするインデックスをインデックスにすることはできません。対応する値を格納するのはB-TREEインデックスのみです。また、さまざまなストレージエンジンがさまざまな方法でインデックスのカバーを実装しており、すべてのストレージエンジンがインデックスのカバーをサポートしているわけではありません(MemoryとFalconはサポートしていません)。

インデックスでカバーされるクエリの場合、EXPLAINを使用するときの[追加]列に「インデックスの使用」が表示されます。たとえば、Sakilaのインベントリテーブルには、複合インデックス(store_id、film_id)があります。これらの2つの列にのみアクセスする必要があるクエリの場合、MySQLは次のようにインデックスを使用できます。

ほとんどのエンジンでは、クエリによってアクセスされる列がインデックスの一部である場合にのみ、インデックスがカバーされます。ただし、InnoDBはこれに限定されません。InnoDBのセカンダリインデックスは、リーフノードのプライマリキーの値を格納します。したがって、sakila.actorテーブルはInnoDBを使用し、last_nameにインデックスがあるため、インデックスは、actor_idにアクセスする次のようなクエリをカバーできます。

3.3、インデックスを使用して並べ替え

MySQLでは、順序付けられた結果セットを生成する2つの方法があります。1つはファイルソートを使用する方法、もう1つはインデックス順にスキャンする方法です。インデックスを使用した並べ替え操作は非常に高速であり、同じインデックスを使用して検索操作と並べ替え操作を同時に行うことができます。インデックスの順序がORDERBYの列の順序と同じであり、すべての列が同じ方向(すべて昇順またはすべて降順)の場合、インデックスを使用して並べ替えることができます。クエリが複数のテーブルを結合する場合、インデックスはORDERBYのすべての列が最初のテーブルの列である場合にのみ使用されます。Filesortは他の場合に使用されます。

MySQLがソートにインデックスを使用できない場合、MySQLは独自のソートアルゴリズム(クイックソートアルゴリズム)を使用してメモリ内のデータをソートします(ソートバッファ)。メモリをロードできない場合は、ディスク上のデータをブロックに分割します。 、次に各データブロックを並べ替えてから、各ブロックを順序付けられた結果セットにマージします(実際には外側の並べ替え)。filesortの場合、MySQLには2つのソートアルゴリズムがあります。

(1)2パススキャンアルゴリズム

実装方法は、最初にソートする必要のあるフィールドと、関連する行データに直接配置できるポインタ情報を取り出し、次にセットメモリ(パラメータsort_buffer_sizeで設定)でソートし、ソート後に完了したら、行ポインタ情報列から必要な情報を再度取り出します。

注:このアルゴリズムは、4.1より前に使用されていたアルゴリズムです。データに2回アクセスする必要があります。特に、2回目の読み取り操作では、多くのランダムI / O操作が発生します。一方、メモリのオーバーヘッドは小さいです。

(2)1つのスキャンアルゴリズム(シングルパス)

このアルゴリズムは、必要なすべての列を一度に取り出し、メモリでソートした直後に結果を出力します。

注:このアルゴリズムは、MySQL4.1以降で使用されています。I / Oの数が減り、効率が向上しますが、メモリのオーバーヘッドも大きくなります。不要な列を削除すると、並べ替えに必要なメモリが大幅に浪費されます。MySQL 4.1以降のバージョンでは、max_length_for_sort_dataパラメータを設定して、MySQLが最初のソートアルゴリズムを選択するか、2番目のソートアルゴリズムを選択するかを制御できます。取り出されたすべての大きなフィールドの合計サイズがmax_length_for_sort_dataの設定よりも大きい場合、MySQLは最初のソートアルゴリズムを使用することを選択します。それ以外の場合は、2番目を選択します。並べ替えのパフォーマンスを可能な限り向上させるために、当然、2番目の並べ替えアルゴリズムを使用することを好みます。そのため、クエリから必要な列のみを抽出する必要があります。

結合操作をソートするときに、ORDER BYが最初のテーブルの列のみを参照する場合、MySQLはテーブルに対してファイルソート操作を実行してから、結合処理を実行します。このとき、EXPLAINは「Usingfilesort」を出力します。それ以外の場合、MySQLは結果セットにクエリを実行する必要があります。結果セットは一時テーブルを生成し、接続の完了後にファイルソート操作が実行されます。このとき、EXPLAINは「一時の使用;ファイルソートの使用」を出力します。

3.4、インデックスとロック

インデックスは、クエリがロックするタプルの数を減らすことができるため、InnoDBにとって非常に重要です。MySQL 5.0では、トランザクションがコミットされるまでInnoDBのロックが解除されないため、これは非常に重要です。2つの理由があります。1つは、InnoDBの行レベルのロックのオーバーヘッドが非常に効率的であっても、メモリのオーバーヘッドも小さいですが、それでもオーバーヘッドがあります。第二に、不要なタプルをロックすると、ロックのオーバーヘッドが増加し、同時実行性が低下します。

InnoDBは、アクセスする必要のあるタプルのみをロックし、インデックスを使用すると、InnoDBがアクセスするタプルの数を減らすことができます。ただし、この目標は、ストレージエンジン層でこれらの不要なデータを除外することによってのみ達成できます。インデックスがInnoDBにそれを許可しない場合(つまり、フィルタリングの目的を達成できない場合)、MySQLサーバーはInnoDBによって返されたデータに対してWHERE操作のみを実行できます。現時点では、これらのタプルをロックすることは避けられません。 InnoDBはこれらの要素グループをロックしました。サーバーのロックを解除できなくなりました。

例を見てみましょう:

クエリは2〜3のデータのみを返し、実際には1〜3のデータに対して排他ロックがあります。MySQLのクエリプランは範囲クエリにインデックスのみを使用するため、InnoDBはタプル1をロックします(フィルタリングなしで、WHEREの2番目の条件はインデックスを使用できなくなります)。

ストレージエンジンがインデックスの先頭から開始し、actor_id <4がfalseになるまですべての行をフェッチし、サーバーがInnoDBにタプル1を削除するように指示できないことを示します。行1がロックされていることを証明するために、別の接続を作成し、次の操作を実行します。

クエリは一時停止され、最初に接続されたトランザクションがロックの解放をコミットするまで実行されません(この動作はステートメントベースのレプリケーションに必要です)。上に示したように、インデックスを使用すると、InnoDBは不要なタプルをロックします。さらに悪いことに、クエリがインデックスを使用できない場合、MySQLは、本当に必要かどうかに関係なく、全表スキャンを実行し、各タプルをロックします。

おすすめ

転載: blog.csdn.net/Linuxhus/article/details/112392001