この記事では、Mysqlで使用される「インデックスの種類」、「インデックスを正しく使用するための原則」、「インデックスを最適化する方法」、「2つのストレージエンジンInnoDBおよびMyISAMインデックスのデータレイアウトの原則」について説明します。
インデックスタイプ
インデックスについて話す前に、インデックスとは何かについて話しましょう。インデックス作成の個人的な理解は、インデックス作成はデータのクエリを高速化するデータ構造であるということです。
したがって、インデックスは一種のデータ構造であり、その役割はこのデータ構造の役割を果たし、クエリの効率を高速化することです。たとえば、B +ツリーデータ構造は、インデックスを編成するためにInnoDBストレージエンジンで使用されます。
Mysqlには多くの種類のインデックスはありません。異なる種類のインデックスには異なる機能があります。また、インデックスの機能間には相互関係があります。Mysqlのインデックスは、主に次のカテゴリに分類されます。
- 「プライマリキーインデックス」(PRIMARYKEY):プライマリキーインデックスは通常、テーブルの作成時に指定されます。「テーブルにはプライマリキーインデックスが1つしかない」という特徴があり、「一意で空でない」という特徴があります。
- 「一意のインデックス」(UNIQUE):一意のインデックスには一意であるという特性があり、テーブルの作成時に指定するか、テーブルの作成後に指定できます。
- 「通常のインデックス」(INDEX):通常のインデックスの唯一の機能は、クエリを高速化することです。
- 「複合インデックス」(INDEX):複合インデックスは「マルチフィールドインデックス」を作成することです。この概念は、上記の単一列インデックスと比較して、複合インデックスクエリは「左端のプレフィックス原則」に従うというものです。
- 「フルテキストインデックス」(FULLTEXT):フルテキストインデックスは、「フルテキスト検索」とも呼ばれるいくつかの大きな「テキストフィールド」用に作成されたインデックスです。
- 「クラスター化インデックス」と「非クラスター化インデックス」:クラスター化インデックスと非クラスター化インデックスの概念は、包含と包含の関係に属する上記の概念よりも大きくなります。例:InnoDBのプライマリキーインデックスはクラスター化されたインデックスを使用します。
テーブルのすべてのインデックスを表示する場合は、次のsqlを実行して表示できます。
show index from 表名
たとえば、次の図に示すように、自分のテストテーブルのインデックスを見てください。Key_nameはインデックスの名前を表し、Column_nameはインデックスのフィールドを表します。
上記はメインインデックスの概念の概要であり、以下はこれらのメインインデックスの特徴と使用法の詳細な紹介です。
プライマリキーインデックス
プライマリキーインデックスは、InnoDBストレージエンジンで最も一般的なタイプのインデックスです。テーブルにはプライマリキーインデックスがあり、インデックス付きフィールドをnullで一意にすることはできません。
通常、テーブルを作成するときに、RIMARY KEYを使用してプライマリキーインデックスを指定できます。InnoDBストレージエンジンでは、テーブルの作成時にプライマリキーインデックスが主観的に作成されていない場合、Mysqlはテーブルに一意のインデックスがあるかどうかを確認します。ある場合は、「空でない一意のインデックス」がプライマリキーインデックスです。
いいえ、デフォルトでプライマリキーインデックスとして6バイトスペースの自動拡張プライマリキーが生成されます。対応するプライマリキー値は、テーブル名からselect_rowidでクエリできます。
MyISAMストレージエンジンにはプライマリキーインデックスがない場合があります。データを格納するためのMyISAMとInnoDBの構造には明らかな違いがあります。これについては、後の章で詳しく説明します。
一意のインデックス
一意のインデックスとプライマリキーインデックスの違いは、一意のインデックスを空にすることができることです。複合インデックスにある場合、作成された列の値が一意である限り、
実際には、一意のインデックスはデータの一意性を確保するために使用されます。データをすばやくクエリするだけの場合は、通常のインデックスを使用することもできるため、一意のインデックスはその一意性を反映することに重点を置いています。
実際のビジネスシナリオでは、一部のターゲットフィールドを一意にする必要がある場合は、一意のインデックスを使用できます。一意のインデックスを作成するには、3つの方法があります。
(1)次のように、テーブルの作成時に1つが指定されます。
CREATE TABLE user(
id INT PRIMARY KEY NOT NULL,
name VARCHAR(16) NOT NULL,
UNIQUE unique_name (name(10))
);
(2)次のように、テーブルの作成後に作成することもできます。
CREATE UNIQUE INDEX unique_name ON user(name(10));
(3)次のように、テーブル構造を変更して作成します。
ALTER user ADD UNIQUE unique_name ON (name(10))
ここで注意すべき詳細の1つは、作成された名前フィールドの長さが16文字であり、作成されたインデックスの長さが10文字であるということです。これは、誰の名前も10文字を超えないため、インデックスの長さを短くします。インデックスが占めるスペースのサイズを減らすことができます。
通常のインデックス
通常のインデックスの唯一の機能は、データクエリを高速化することです。通常、通常のインデックスは、クエリステートメントWHEREおよびORDERBYの後のフィールドに対して作成されます。
共通インデックスを作成する方法も3つあります。これは、以下に示すように、キーワードUNIQUEがINDEXに置き換えられることを除いて、一意のインデックスを作成する方法と基本的に同じです。
// 创建表的时候创建
CREATE TABLE user(
id INT PRIMARY KEY NOT NULL,
name VARCHAR(16) NOT NULL,
INDEX index_name (name(10))
);
// 创建表后创建
CREATE INDEX INDEX index_name ON user(name(10));
// 修改表结构创建
ALTER user ADD INDEX index_name ON (name(10))
インデックスを削除する場合は、次のsqlを実行してインデックスを削除できます。
DROP INDEX index_name ON user;
複合インデックス
複合インデックスは、複数のフィールドを使用してインデックスを作成します。複合インデックスは、「テーブルに戻るクエリ」を回避できます。複数のフィールドの単一列インデックスと比較して、複合インデックスのクエリ効率は高くなります。
複合インデックス(ジョイントインデックス)を作成する方法は、フィールドの数が多く、次のsqlが作成されることを除いて、上記の通常のインデックスを作成する方法と同じです。
// 其它方式和上面的一样,这里就只列举修改表结构的方式创建
ALTER TABLE employee ADD INDEX name_age_sex (name(10),age,sex);
テーブルクエリに戻る
テーブルクエリに戻るのは何ですか?テーブルに戻るクエリは、単に「セカンダリインデックスを介してデータをクエリし、完全なデータ行を取得できないため、データ行を取得するにはプライマリキーインデックスを再度クエリする必要がある」ことを意味します。
InnoDBストレージエンジンでは、インデックスは「クラスター化インデックス」と「セカンダリーインデックス」に分けられ ます。プライマリーキーインデックスはクラスター化インデックスであり、その他のインデックスはセカンダリーインデックスです。
クラスター化インデックスのリーフノードは完全なデータ行を格納しますが、セカンダリインデックスのリーフノードは完全なデータ行を格納しません。
前述のように、InnoDBテーブルにはプライマリキーインデックスが必要です。インデックスはスペースを占有しますが、インデックスはバイナリ検索アルゴリズムに準拠しており、データは非常に高速に検出されます。
上記のemployeeテーブルが引き続きプライマリキーインデックスIDであり、通常のインデックス名であるとすると、InnoDBには2つのB +ツリーがあり、1つはプライマリキーインデックスツリーです。
プライマリキーインデックスツリー
次の図に示すように、プライマリキーインデックスツリーのリーフノードは完全なデータ行を格納し、もう1つは名前フィールドを持つセカンダリインデックスツリーです。
このsqlを実行する場合:select name、age、sex from employee where id = 'as';最初にセカンダリインデックスのクエリを実行します。クエリ名= 'as'の場合、プライマリキーは50で、クエリはプライマリキーに基づいています。完全なデータ行を取得するためのプライマリキーインデックスツリーの具体的な実行プロセスは次のとおりです。
表の概略に戻る
これはテーブルに戻るクエリです。テーブルに戻るクエリは2回クエリされるため、クエリの効率が低下します。テーブルに戻るクエリを回避するために、完全なデータを1回だけ取得できますか?
インデックスカバレッジ
一般的な方法は、「結合されたインデックス(ジョイントインデックス)を作成し、インデックスカバレッジを「実行」する」ことです。インデックスカバレッジとは何ですか?インデックスカバレッジとは、「インデックスのリーフノードにはクエリ対象のデータがすでに含まれているため、クエリのためにテーブルに戻る必要がない」ことを意味します。
次のsqlを実行するとします。selectname、age、sex from employee where name = 'as';通常のインデックスには、インデックスを確立するためのnameフィールドしかないため、必然的にテーブルクエリに戻ります。
クエリの効率を上げるために、(名前)「単一列インデックスから共同インデックスへのアップグレード」(名前、年齢、性別)が異なります。
ジョイントインデックスが確立されているため、名前、年齢、性別の3つの値がセカンダリノードのリーフステージに同時に存在し、必要なデータが一度に取得されます。これにより、テーブルに戻ることが回避されますが、すべてのソリューションがそうではありません。完璧です。
ジョイントインデックスの特定のデータ行の名前の値または経過時間が特定の日に変更された場合、プライマリキーインデックスとジョイントインデックスを同時に維持する必要があります。これにより、メンテナンスコストとパフォーマンスのオーバーヘッドが増加します。
以前のデータ変更と比較して、プライマリキーインデックスを維持するだけで済みます。ジョイントインデックスを作成すると、2つのツリーを同時に維持する必要があり、データの挿入と更新の操作に影響するため、完璧なソリューションはありません。 。
左端のプレフィックスの原則
単一列のインデックスは、インデックス列の順序に従ってB + Tree構造を編成することがわかっていますが、ジョイントインデックスはB + Treeをどのように編成しますか?
ジョイントインデックスは、インデックスを作成するときに実際には左端に従ってソートされます。これは「左端のプレフィックス原則」です。たとえば、テーブルには次のデータがあります。
nameagesexad23男性bc21男性bc24女性bc25男性de21女性
上の図に示すように、ジョイントインデックスの名前フィールドが最初に配置されるため、名前は完全に順序付けられますが、名前が同じ場合にのみ、年齢フィールドは順序付けられません。たとえば、name = 'bc'、次にageフィールドのインデックスソートは完全に順序付けられています。
したがって、共同インデックスでは、次のルールを使用してクエリを実行した場合にのみインデックスを使用できることがわかります。
- 名前、年齢、性別
- 名前と年齢
- 名前
Mysqlの最下層にはクエリオプティマイザがあるため、sqlの実行時にテーブル全体のスキャンがインデックスよりも効率的である場合は、テーブル全体のスキャンが使用されると判断されます。
クエリを実行するときに、age> = 23、sex = 'male'; 2つのフィールドをクエリ条件として使用するとしますが、名前がわからないまま年齢が順序付けられていないため、名前フィールドは使用しません。
年齢> = 23の場合、条件はさまざまな名前の条件を満たしている可能性があるため、インデックスを使用する方法はありません。これがインデックスを実装する理由でもあります。「順番に検索し、インデックスの順序を最大限に活用する」に従う必要があります。"。
名前、年齢、性別の3つのフィールドにそれぞれ3つの単一列インデックスを作成する場合、3つのインデックスツリーを作成するのと同じです。インデックスツリーを使用する場合よりも、クエリの効率を想像できます。アップ。
左端の名前フィールドを使用してもインデックスが使用されない状況があります。たとえば、「%d%」のようなWHERE名。この種のファジークエリのような条件は、インデックスを無効にします。
「クエリ文字列も左端のプレフィックスの原則に従う」と理解できます。文字列のクエリは、文字列の文字を1つずつ一致させることです。「文字列の左端が%の場合、それは不確実な文字列を意味します。したがって、インデックスの順序を使用する方法はありません」。
ただし、次のように変更した場合: 'd%'のようなWHERE名;インデックスを使用できます。左端の文字列が決定されるため、これは「一致する列のプレフィックス」と呼ばれます。
実際のビジネスシナリオでの共同インデックスの作成では、「認識度の高いフィールドを前面に出し、インデックスのヒット率を向上させ、インデックスを最大限に活用する必要があります。」
インデックスプッシュダウン
Mysql 5.6バージョンは、「クエリの最適化、主に同様のキーワードのクエリの最適化」というインデックスプッシュダウンの原則を提唱していますが、インデックスプッシュダウンとは何ですか?
次のSQLクエリを実行する場合は、デモンストレーションを通じてその概念を説明するか、元の従業員テストテーブルを使用します。SELECT* from user where name like '张%' and age = 40;
インデックスのプッシュダウンがない場合、実行プロセスを次の図に示します。
クエリは年齢フィールドを直接無視し、名前クエリの開始時にid = 5とid = 7の結果をMysqlサーバーに返し、テーブルに戻るクエリを2回実行します。
上記のクエリ操作でインデックスプッシュダウンを使用する場合、実行プロセスは次のようになります。
Mysqlはage = 40のクエリ条件をストレージエンジンに渡し、age = 50のデータ行を再度除外するため、テーブルに戻る回数が1回になり、クエリの効率が向上します。
要約すると、インデックスプッシュダウンとは、SQLクエリの実行時に、インデックス列の判断条件の一部がストレージエンジンに渡され、ストレージエンジンが条件が満たされているかどうかを判断し、条件を満たすデータのみがMysqlサーバーに返されることを意味します。
フルテキストインデックス
フルテキストインデックスはフルテキスト検索とも呼ばれ、次のsqlを使用してフルテキストインデックスを作成できます。ALTERTABLEemployee ADD FULLTEXT fulltext_name(name);またはCREATEINDEX。
フルテキストインデックスは、主にCHAR、VARCHAR、TEXTなどのテキストフィールドに効果的です。「いいえ」と言う人もいますが、likeキーワードを使用してテキストをクエリすることもできますか?
一般インデックス(単一列インデックス)クエリは、フィールドコンテンツの最初の文字列の取得を高速化することしかできません。テキストで構成される複数の単語のクエリである場合、一般インデックスは何もできません。
インデックスが作成されると、それを変更する方法はありません。インデックスを変更する場合は、インデックスを再構築する必要があります。次のSQLを使用して、インデックスを削除できます。DROPINDEX fulltext_name ON employee;
クラスター化インデックスと非クラスター化インデックス
クラスター化インデックスと非クラスター化インデックスはストレージエンジンの概念に関連しており、スコープは上記のインデックスタイプを含めて比較的大きくなります。
「クラスター化インデックスとは、行データ全体がリーフノードに格納され、インデックスとデータが一緒に格納されることを意味します。非クラスター化インデックスのインデックスファイルとデータファイルは分離されているため、クエリデータはもう一度クエリされます。」
したがって、クラスター化インデックスのクエリ速度は、非クラスター化インデックスのクエリ速度よりも速くなります。Mysqlストレージエンジンでは、「InnoDBはクラスター化インデックスをサポートし、MyISAMはクラスター化インデックスをサポートせず、MyISAMは非クラスター化インデックスをサポートします。」
クラスター化されたインデックス
InnoDBのクラスター化インデックスを見てみましょう。前述のように、InnoDBにはプライマリキーがあります。プライマリキーはクラスター化インデックスをサポートするために使用されます。クラスター化インデックスの構造図は、おおよそ次の図に示すとおりです。
InnoDBでの最良のプライマリキーの選択は、AUTO_INCREMENT列を自動インクリメントのプライマリキーとして指定することです。UUIDをランダムなプライマリキーとして使用する人もいます。
インデックスは秩序を維持する必要があるため、ランダムなプライマリキーを使用する場合、プライマリキーの挿入では配置に適した場所を見つける必要があり、プライマリキーのインデックスツリーを維持するコストが高くなります。
逆に、自己インクリメントの主キー、主キーは自己増加して大きくなり、主キーのインデックスツリーを維持するためのコストが小さくなるため、ランダムな主キーはできるだけ避けてください。
非クラスター化インデックス
MyISAMは非クラスター化インデックスを使用します。新しいデータが挿入されると、そのデータは順番にディスクに書き込まれ、データの各行には行番号が付けられ、小さいものから徐々に大きくなります。
MyISAMがプライマリキーインデックスを作成すると、形成されるプライマリキーインデックスツリーの構造図が次の図に示されます。
プライマリキーインデックスでは、データも空ではなく一意です。プライマリキーインデックスツリーには、データ行の行番号が格納されます。データをクエリする場合、プライマリキーインデックスクエリは行番号を見つけてから、行番号でデータを取得する必要があります。
非プライマリキーインデックスはプライマリキーインデックスと同じです。リーフノードにも行番号が格納されます。唯一の違いは、非プライマリキーインデックスが空でなく、一意である必要がないことです。
次の図に示すように、比較チャートを使用して、「InnoDB(クラスター化インデックス)」 と 「MyISAM(非クラスター化インデックス)」のインデックスデータレイアウトを比較できます。
そういえば、「InnoDB(クラスター化インデックス)」 と 「MyISAM(非クラスター化インデックス)」を誰もが 明確に理解している必要があると思います。以下はインデックスの最適化であり、これも日々の開発です。最も密接に関連しています。
インデックス作成の原則と最適化
インデックスを正しく使用するには、インデックスを正しく作成し、インデックスの正しいクエリを使用し、インデックスを無効にしないことが必要です。したがって、インデックスの設計と最適化の原則は、次の原則に従う必要があります。
- インデックス列は式に表示されるべきではありません。これにより、インデックスが失敗します。例:"SELECT ...... WHERE id + 1 = 5" ;
- 関数のパラメーターとしてインデックス列を使用しないでください。
- インデックス列でlikeキーワードを使用しないようにしてください。例:"SELECT ...... WHERE name like '%d%'" ;
- 数値インデックス列を条件付きクエリの文字列タイプとして扱わないでください。例:"SELECT ...... WHERE id = '35'" ;
- NOT IN、<>、!=の条件ではインデックスを使用しないようにしてください。
- インデックス列のフィールドにNULL値を含めないでください。NULL値はインデックスを無効にします。空の文字列 ''や0などの特殊な文字を使用してNULL値を置き換えることができます。
- ジョイントインデックスのクエリは、左端のプレフィックスの原則に従う必要があります。
- 一般的に、差が比較的大きいインデックスが設定されますが、ジョイントインデックスでは、差が比較的大きい(認識度が高い)ことが一番上に配置され、インデックスのヒット率が向上します。
- インデックスの冗長性を回避するために、インデックスのサイズは中程度で、大きすぎないようにする必要があります。
総括する
インデックスは、私たちが仕事でよく使用するデータクエリ方法です。インデックスを正しく使用すると、クエリの効率を大幅に向上させることができます。
- 一方では、インデックスにより、インデックスサーバーがスキャンする必要のあるデータ行の数が減ります。元の完全なテーブルをスキャンし、特定のデータ構造を使用すると、データ行をすばやく見つけることができます。
- 一方、順序付きインデックスを使用すると、並べ替えが回避され、元のランダムIO操作が順次IO操作に変換され、順序どおりに実行されます。
ただし、インデックスは完全ではなく、独自の欠点があります。インデックスを誤って使用すると、インデックスが多くのスペースを占めることになります。インデックスが多いほど、インデックスファイルが拡張され、クエリのパフォーマンスに深刻な影響を及ぼします。
データの挿入、更新、削除には、データの維持に加えて、インデックスファイルを維持する必要があります。これは、これらの操作のパフォーマンスにも影響しますが、クエリの頻度がデータの更新と挿入よりもはるかに高いビジネスシナリオでは、インデックスの方が適しています。 。
次の記事は、専門家ではない主題、著者LiDuからのものです