MySQL (上級、インタビュー) - (MySQL 最適化 2) MySQL インデックスの最適化について説明する

       1. はじめに

        インデックスは、より効率的なデータクエリのために確立されます。データベースのインデックスを正しく使用する方法と、インデックスが無効になるのを防ぐ方法。私たちが守るべき基本原則は次のとおりです。

まず、MySQL ステートメントの実行順序を確認してみましょう。

SQL の実行シーケンス SQL
     を最適化したい場合は、SQL の実行シーケンスを明確に把握する必要がありますが、このように Explain を使用すると、半分の労力で 2 倍の結果を得ることができます。

完全な SQL ステートメント

select distinct 
        <select_list>
from
    <left_table><join_type>
join <right_table> on <join_condition>
where
    <where_condition>
group by
    <group_by_list>
having
    <having_condition>
order by
    <order_by_condition>
limit <limit number>
3.2 SQL执行顺序

執行順序

1、from <left_table><join_type>
2、on <join_condition>
3、<join_type> join <right_table>
4、where <where_condition>
5、group by <group_by_list>
6、having <having_condition>
7、select
8、distinct <select_list>
9、order by <order_by_condition>
10、limit <limit_number>

 SQL の実行シーケンスからわかるように、最初の選択肢は、件名によって条件をフィルタリングしてクエリ データの量をできるだけ減らし、その後、選択を通じてクエリ統計を実行することです。つまり、最適化できるものは第7条以前に集中しているのです。
 

2. ケースデモンストレーションインデックスの最適化

準備

2.1 テーブルの作成

REATE TABLE `staffs` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `pos` varchar(20) NOT NULL COMMENT '职位',
  `add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2 基本データの挿入

INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('1', '王二', '25', '总经理', '2018-05-22 09:45:44');
INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('2', 'July', '25', '实习生', '2018-05-22 09:45:58');
INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('3', '李四', '20', '实习生', '2018-05-22 09:46:04');
INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('4', '王玉', '21', '老板娘', '2018-05-22 09:46:17');
INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('5', '王五', '22', '服务员', '2018-05-22 09:46:26');
INSERT INTO `test`.`staffs` (`id`, `name`, `age`, `pos`, `add_time`) VALUES ('6', '赵六', '80', '传菜生', '2018-05-22 09:46:45');

2.3 インデックスの作成

ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos (NAME, age, pos);

2.4 クエリインデックス

SHOW INDEX FROM staffs

ここに画像の説明を書きます

前回の記事と合わせてMySQLを見てみる 実践分析- (MySQLの最適化1) 詳しい解説

MYSQL EXPLAIN は、インデックスを使用して、 EXTRA
でインデックスが使用されている限り where を使用して、 USING INDEX、USING WHERE、USING INDEX CONDITION を解析します。よく見かけるのですが、インデックス条件の使用は、mysql5.6 以降に追加された新機能です。

using Index : カバリングインデックスを使用する場合に表示されます
using where : インデックスを使用して検索する場合、必要なデータをクエリするためにテーブルに戻る必要があります
using Index Condition : インデックスを使用して検索しますが、クエリを実行する必要がありますデータをクエリするためにテーブルに戻る
. using Index & using where : 検索ではインデックスが使用されますが、必要なデータはインデックス列で見つかるため、データをクエリするためにテーブルに戻る必要はありません。

3. インデックス失敗最適化のケース

3.1 完全値一致私のお気に入り

 完全な値の一致とは、クエリしたいステートメントのフィールドと順序が、確立されたインデックスのフィールドと順序とまったく同じであることを意味します。そうでない場合、インデックスは失敗します。

正しい使い方

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';

ここに画像の説明を書きます

 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25;

ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生'

ここに画像の説明を書きます

 インデックスの失敗

EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = '实习生';

ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE pos = '实习生';

ここに画像の説明を書きます

 要約:上記の結果から、名前、年齢、位取りの順にインデックスを作成する場合、クエリ条件が名前で始まらない場合、インデックスは失敗することがわかります。一言でまとめるとこうなります。带头大哥不能死!

3.2 ベストレフトプレフィックスルール

複数の列にインデックスが付けられている場合は左端のプレフィックス ルールに従う必要があります。つまり、クエリはインデックスの左端の先頭列から開始されます不跳过索引中的列

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND pos = '实习生';

ここに画像の説明を書きます

上の図に示すように、2 つのインデックス フィールドが一致する場合、key_len は少なくとも 74 より大きく、ref は 2 const である必要があります。クエリ ステートメントは name フィールドのインデックスのみを使用し、後続のインデックスは無効であることがわかります。

✈ まとめ:上記の結果から、名前、年齢、役職の順にインデックスを構築した場合、クエリ条件が名前で始まるものの順序が異なっている場合、それ以降のインデックスは無効になることがわかります。一言でまとめると、「真ん中の兄弟は離れられない!」です。

 2.3 インデックス列に対して操作を実行しない

        ここで計算、関数、(自動または手動の) 型変換を含む操作を行うと、インデックスが失敗し、テーブル全体のスキャンが行われます。

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';

ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE left(NAME,4) = 'July';

ここに画像の説明を書きます

2.4 ストレージ エンジンはインデックス内の範囲条件の右側の列を使用できません

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生';

 ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = '实习生';

ここに画像の説明を書きます

  概要: age > 25 などの範囲検索を使用すると、タイプが range に変更され、key_len が 78 に等しくなります。これは、name インデックスと age インデックスが使用されていることを示しますが、age インデックスは範囲ソートに変更され、正確ではありません。検索により、後続の pos インデックスは無効になります。要約は次のとおりです。范围之后全失效!

2.5 カバリングインデックスを使ってみる

 インデックス付きクエリを使用してみてください。つまり、インデックス列とクエリ列に一貫性があり、選択を減らすことができます *

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生';

ここに画像の説明を書きます

EXPLAIN SELECT name,age,pos FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生';

ここに画像の説明を書きます

EXPLAIN SELECT name,age,pos FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = '实习生';

ここに画像の説明を書きます

 2.6 MySQL は等しくない (!= または <>) を使用するとインデックスを使用できず、テーブル全体のスキャンが発生します。

EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';

ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE name != 'July';

ここに画像の説明を書きます

EXPLAIN SELECT * FROM staffs WHERE name <> 'July';

ここに画像の説明を書きます

 2.7 インデックスがnullまたはnullでない場合でも使用できません

2.8 like がワイルドカード文字 ('%abc...') で始まる場合、インデックスが失敗すると、テーブル全体のスキャンになります。

2.9 一重引用符のない文字列インデックスは失敗する

接続時に使用するとインデックスが無効になるため、2.10 以下で使用してください。

EXPLAIN SELECT * FROM staffs WHERE name = 'July' or age = 25

ここに画像の説明を書きます

 3.3 一般的な提案
単一キー インデックスの場合は、現在のクエリに対してより優れたフィルター機能を持つインデックスを選択するようにしてください。結合インデックスを選択する場合は
、現在のクエリで最もフィルター機能が高いフィールドがインデックス フィールドの順序にある​​必要があります。インデックスを
組み合わせる場合は、現在のクエリの where 句により多くのフィールドを含めることができるインデックスを選択するようにしてください。
統計情報を分析したり、クエリの記述方法を調整したりして、適切なインデックスを選択してください
。ヒントのまとめ
 

全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不能空值还有OR,索引失效要少用;
VAR引号不可丢,SQL高级也不难;

元のリンク: [MySQL Advanced] (4) インデックス最適化インデックスの失敗_Wang Honyu のブログ-CSDN ブログ_MySQL 範囲クエリ インデックスの失敗

おすすめ

転載: blog.csdn.net/u010445301/article/details/126235856