2022年SQL最適化大泉まとめと詳細解説

1. MySQL の基本構造

1) MySQL インフラストラクチャ図

左側のクライアントはクライアントと見なすことができます. 私たちがよく使用する CMD ブラック ウィンドウ、学習によく使用する WorkBench、企業がよく使用する Navicat ツールなど、多くのクライアントがあります. それらはすべて、クライアント。右側の山はサーバー(MySQL サーバー)とみなすことができ、サーバーをさらに sql レイヤーとストレージ エンジン レイヤーに分けます。

データが照会されると、エグゼキュータに返されます。一方では、エグゼキューターが結果をクエリ キャッシュに書き込み、次に再度クエリを実行するときに、クエリ キャッシュから直接データを取得できます。一方、結果を直接クライアントに返します。

2) データベースを照会するエンジン

①ショーエンジン;

② 「%storage_engine%」などの変数を表示します。

3) データベース オブジェクトのストレージ エンジンを指定する

create table tb(
    id int(4) auto_increment,
    name varchar(5),
    dept varchar(5),
    primary key(id)
) engine=myISAM auto_increment=1 default charset=utf8;

2.SQL の最適化

1) なぜ SQL の最適化が必要なのですか?

複数テーブルの結合クエリやサブクエリなどの操作を行う場合、SQL 文の記述が不十分なため、サーバーの実行時間が長くなり、結果を待つ時間が長くなります。これに基づいて、SQL を最適化する方法を学ぶ必要があります。

2) MySQL の書き込み処理と解析処理

①執筆の流れ

select dinstinct  ..from  ..join ..on ..where ..group by ..having ..order by ..limit ..

②分析プロセス

from .. on.. join ..where ..group by ..having ..select dinstinct ..order by ..limit ..

mysql の解析プロセスを詳しく説明している Web サイトを提供してください。

https://www.cnblogs.com/annsshadow/p/5037667.html

3) SQL の最適化 - 主にインデックスの最適化

SQL を最適化するために最も重要なことは、SQL インデックスを最適化することです。

インデックスは、辞書のディレクトリに相当します。辞書ディレクトリを使用して漢字を検索するプロセスは、SQL インデックスを使用してレコードを検索するプロセスと同じです。索引を使用すると、レコードを簡単かつ迅速に見つけることができます。

①インデックスとは?

インデックスは、MySQL が効率的にデータを取得するのに役立つ [データ構造] です。インデックスはツリー構造で、MySQL では一般的に [B+ ツリー] が使用されます。

②索引図(ここでは、索引を分かりやすくするために二分木を使用しています)

ツリー構造の特徴は、親要素よりも小さい子要素が左側に配置され、親要素よりも大きい子要素が右側に配置されることです。

この図はインデックスを分かりやすくするためのもので、実際の[B+ツリー]の説明は後述します。

インデックスはどのようにデータを検索しますか? 2 つの単語 [指し示す] 上の図では、右のツリー構造に似たインデックスを age 列に割り当てています。mysql テーブルの各行レコードには、インデックス内の age=50 などのハードウェア アドレスがあり、ソース テーブル内の行の識別子 (「ハードウェア アドレス」) を指します。つまり、ツリー インデックスは、ソース テーブルの各行のハードウェア アドレスとのマッピング関係を確立します. インデックスを指定すると、このマッピング関係も確立されます. これが、インデックスを介してソース テーブルをすばやく見つけることができる理由です.に記載されている理由。

クエリステートメント [select * from student where age=33] を例に取ります。インデックスを追加しない場合は、ソース テーブルを上から下にスキャンし、5 行目をスキャンすると、探したい要素が見つかり、合計 5 回のクエリが実行されます。インデックスを付けると木構造で直接検索されます.33が50より小さい場合は左から23に問い合わせます.33が23より大きい場合は再度右に問い合わせます.今度は33が見つかり、インデックス全体が終了します。合計 3 回の検索が実行されました。とても便利でしょうか? この時に age=62 を求める必要がある場合、「インデックスを追加する」前後の検索数の変化を考えることができます。

4) 索引付けの短所

1. データ量が多い場合、インデックスは非常に大きくなり (もちろん、ソース テーブルと比較すると、まだかなり小さいです)、メモリ/ハードディスクに格納する必要もあります (通常は、ハードディスク)、一定量のデータを占有します. メモリスペース/物理スペース.

2. 索引はすべての状況に適しているわけではありません: a. 少量のデータ; b. 頻繁に変更されるフィールドは索引付けに適していません; c. ほとんど使用されないフィールドは索引付けする必要はありません;

3. インデックスはデータクエリの効率を向上させますが、「追加、削除、および変更」の効率を低下させます。インデックスを使用しない場合は、ソース テーブルを操作してデータを追加、削除、変更するだけですが、インデックスを追加する場合は、ソース テーブルを変更するだけでなく、インデックスを再度変更する必要があります。 、これは非常に面倒です。それでも、使用するもののほとんどはクエリであり、「クエリ」はプログラムのパフォーマンスに大きな影響を与えるため、インデックスを追加することは良いことです。

5) 索引付けの利点

1. クエリ効率を改善します (IO 使用量を減らします)。インデックスが作成されると、クエリの数が減ります。

2. CPU 使用率を減らします。たとえば、[...order by age desc] などの操作では、インデックスが追加されていない場合、ソース テーブルが並べ替え操作のためにメモリに読み込まれ、多くのリソースが消費されます。しかし、インデックスを使用した後、最初のインデックス自体が小さくなり、2 番目のインデックス自体が並べ替えられ、左側のデータが最小になり、右側のデータが最大になります。

6) B+ツリー図

MySQL のインデックスは B+ ツリー構造を使用します。

B+ ツリーに関する注意:

まず、Btreeは一般的に「B+木」を指し、すべてのデータは葉ノードに格納されます。上の図の場合、下の 3 番目のレイヤーはリーフ ノードに属し、実データ部分はリーフ ノードに格納されます。では、レイヤー 1 と 2 のデータはどうでしょうか。回答: 26 未満の場合は P1、26 から 30 の間の場合は P2、30 より大きい場合は P3 など、ポインター ブロックを分割するために使用されます。

次に、3 層の [B+ ツリー] には数百万のデータを格納できます。どうやってそんなに多くのデータを手に入れたのですか?「ノード数」を増やします。グラフには 3 つのノードしかありません。

最後に、[B+ ツリー] の任意のデータをクエリする回数は n 回で、n は [B+ ツリー] の高さを表します。

3.分類と索引の作成

1) 指数分類

単一値インデックス

一意のインデックス

複合インデックス

①一価インデックス

テーブル内のフィールドを使用して、単一値インデックスを作成します。多くの場合、テーブルには複数のフィールドがあります。これは、実際のニーズに応じて作成されるインデックスを実際に各列で作成できることを意味します。もう 1 つの注意点は、テーブルが複数の「単一値インデックス」を作成できることです。

テーブルに年齢フィールドと名前フィールドの両方がある場合、テーブルに 2 つの単一値インデックスがあるように、年齢と名前にそれぞれ単一値インデックスを作成できます。

②ユニークインデックス

また、テーブル内のフィールドを使用して、単一値インデックスとは異なる単一値インデックスを作成します。一意のインデックスが作成されるフィールド内のデータは、重複する値を持つことはできません。年齢のように、同じ年齢の人がたくさんいるはずです。名前のように、同じ名前の人もいるはずなので、「一意のインデックス」を作成するのは適していません。番号id、学籍番号sidのように、人それぞれ違うので、ユニークなインデックスを作るのに使えます。

③総合指数

複数の列で構成されるインデックス。たとえば、このような「複合インデックス」(名前、年齢)を作成し、最初に名前を使用してインデックスをクエリし、名前が同じ場合は年齢を使用して再度フィルタリングします。注: 複合インデックスのフィールドを使い切る必要はありません. 名前フィールドを使用して必要な結果にインデックスを付ける場合、年齢を使用して再度フィルタリングする必要はありません.

2) 索引を作成する

①文法

構文: テーブル (フィールド) にインデックス タイプのインデックス名を作成します。

テーブル作成ステートメントは次のとおりです。

クエリ テーブルの構造は次のとおりです。

②インデックスを作成する最初の方法

Ⅰ 単一値インデックスを作成する

create index dept_index on tb(dept);

II 一意のインデックスを作成します。ここでは、名前フィールドの値がすべて一意であると想定しています

create unique index name_index on tb(name);

Ⅲ 複合索引の作成

create index dept_name_index on tb(dept,name);

③ インデックスを作成する 2 つ目の方法

最初に以前に作成したインデックスを削除してから、このインデックス作成方法のテストを実行します。

構文: alter table table name add index type index name (フィールド)

Ⅰ 単一値インデックスを作成する

alter table tb add index dept_index(dept);

II 一意のインデックスを作成します。ここでは、名前フィールドの値がすべて一意であると想定しています

alter table tb add unique index name_index(name);

Ⅲ 複合索引の作成

alter table tb add index dept_name_index(dept,name);

④補足説明

フィールドが主キーの場合、そのフィールドはデフォルトで主キー インデックスになります。

主キー インデックスと一意のインデックスは非常に似ています。同じ点: 列内のデータが同じ値を持つことはできません; 異なる点: 主キー インデックスは null 値を持つことはできませんが、一意のインデックスは null 値を持つことができます。

3) インデックスの削除とインデックスのクエリ

①インデックス削除

構文: テーブル名のインデックス index name を削除します。

drop index name_index on tb;

②インデックスクエリ

構文: テーブル名からインデックスを表示します。

show index from tb;

結果は次のとおりです。

4. SQL パフォーマンスの問題の調査

人間による最適化: SQL の実行計画を分析するには、Explain を使用する必要があります。実行計画は、SQL オプティマイザーをシミュレートして SQL ステートメントを実行できます。これは、独自の SQL の品質を理解するのに役立ちます。

SQL オプティマイザーの自動最適化: MySQL の実行原理を最初に説明したとき、MySQL にはオプティマイザーがあることは既に知っていました.SQL ステートメントを記述したときに、SQL オプティマイザーは、記述した SQL ステートメントが十分ではないと判断した場合、自動的に最適化を実行します。より適切な同等の SQL を記述して実行します。

SQL オプティマイザーの自動最適化機能は、[干渉します] 人間による最適化機能です。SQLの実行計画を確認するとき、うまく書かれていない場合は、独自のSQLを最適化します。うまく最適化できたと思っても、最終的な実行計画は最適化された SQL 文に従って実行されず、最適化された SQL を変更して実行されることがあります。

SQL の最適化は確率論的な問題であり、システムが最適化された SQL に従って結果を実行する場合があります (オプティマイザーは、ユーザーが記述した内容が類似していると判断し、ユーザーの SQL には影響を与えません)。オプティマイザは、実行前に最適化された SQL を変更することがあります。

1) 実行計画を表示する

構文: 説明 + SQL ステートメント

例: select * from tb; を説明します。

2)「実行計画」で知っておくべきいくつかの「キーワード」

ID番号

select_type : クエリの種類

テーブル : テーブル

type : タイプ

possible_keys : 予測に使用されるインデックス

key : 実際に使用されるインデックス

key_len : 使用されるインデックスの実際の長さ

ref : テーブル間の参照

rows : インデックスを通じてクエリされたデータの量

Extra : 追加情報

テーブル ステートメントを作成し、データを挿入します。

# 建表语句
create table course
(
    cid int(3),
    cname varchar(20),
    tid int(3)
);

create table teacher
(
    tid int(3),
    tname varchar(20),
    tcid int(3)
);

create table teacherCard
(
    tcid int(3),
    tcdesc varchar(200)
);

# 插入数据
insert into course values(1,'java',1);
insert into course values(2,'html',1);
insert into course values(3,'sql',2);
insert into course values(4,'web',3);

insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);

insert into teacherCard values(1,'tzdesc') ;
insert into teacherCard values(2,'twdesc') ;
insert into teacherCard values(3,'tldesc') ;

3) 実行計画の説明における共通キーワードの詳細な説明

1) id キーワードの使用方法

①ケース:コース番号2または教員証番号3の教員の情報を問い合わせる:

# 查看执行计划
explain select t.*
from teacher t,course c,teacherCard tc
where t.tid = c.tid and t.tcid = tc.tcid
and (c.cid = 2 or tc.tcid = 3);

結果は次のとおりです。

次に、教師テーブルにいくつかのデータを追加します。

insert into teacher values(4,'ta',4);
insert into teacher values(5,'tb',5);
insert into teacher values(6,'tc',6);

実行計画をもう一度見直してください。

# 查看执行计划
explain select t.*
from teacher t,course c,teacherCard tc
where t.tid = c.tid and t.tcid = tc.tcid
and (c.cid = 2 or tc.tcid = 3);

結果は次のとおりです。

テーブルの実行順序、テーブル数の変更による変更理由:デカルト積。

a   b   c
2   3   4
最终:2 * 3 * 4  = 6 * 4 = 24
c   b   a
4   3   2
最终:4 * 3 * 2 = 12 * 2 = 24

分析: 一貫性はありますが、最終的な実行の数。ただし、中間プロセスでは、6 の一時テーブルと 12 の一時テーブルがあります。明らかに、6 < 12 です。メモリについては、データ量が少ないほど良いため、オプティマイザは確実に最初の実行順序を選択します。 .

結論: id の値は同じで、上から順に実行されます。テーブルの数が変わると、テーブルの実行順序も変わります。

②ケース:SQL講座を担当する先生の説明を問い合わせる(desc)

# 查看执行计划
explain select tc.tcdesc from teacherCard tc 
where tc.tcid = 
(
    select t.tcid from teacher t 
    where  t.tid =  
    (select c.tid from course c where c.cname = 'sql')
);

結果は次のとおりです。

結論: id 値は異なります。id 値が大きいほど、クエリは優れています。これは、ネストされたサブクエリを実行する場合、最初に内側のレイヤーがチェックされ、次に外側のレイヤーがチェックされるためです。

③②を簡単に修正

# 查看执行计划
explain select t.tname ,tc.tcdesc from teacher t,teacherCard tc 
where t.tcid= tc.tcid
and t.tid = (select c.tid from course c where cname = 'sql') ;

結果は次のとおりです。

結論: id 値は同じで異なります。id 値が大きいほど優先度が高く、同じ id 値が上から順に実行されます。

2) select_type キーワードの使用方法: クエリ タイプ

①シンプル:簡単なクエリ

サブクエリやユニオン クエリは含まれません。

explain select * from teacher;

結果は次のとおりです。

②プライマリ:サブクエリを含むメインクエリ(最外層)

③サブクエリ:サブクエリを含むメインクエリ(最外層ではない)

④派生:派生クエリ(一時テーブルを使用)

a. from サブクエリには、テーブルが 1 つしかありません。

b. from サブクエリで、table1 が table2 を結合している場合、table1 が派生テーブルです。

explain select  cr.cname     
from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;

結果は次のとおりです。

⑤ユニオン:上の例のように、ユニオン後のテーブルをユニオンテーブルと呼びます

⑥ ユニオンの結果: ユニオン クエリを使用しているテーブルを教えてください

3) type キーワードの使用に関する指示: 索引タイプ

system と const は理想的であり、インデックス --> 範囲 --> ref のレベルにのみ最適化できます。型を最適化する前提は、インデックスを作成する必要があることです。

①システム

ソース テーブルには 1 つのデータしかありません (実際には、基本的に不可能です)。

派生テーブルには、データのメイン クエリが 1 つだけあります (到達可能な場合もあります)。

②定数

1 つのデータしか検索できない SQL は、主キーまたは一意のインデックス タイプに対してのみ有効です。

explain select tid from test01 where tid =1 ;

結果は次のとおりです。

以前の主キー インデックスを削除した後、この時点で別の通常のインデックスを追加します。

create index test01_index on test01(tid) ;
# 再次查看执行计划
explain select tid from test01 where tid =1 ;

結果は次のとおりです。

③eq_ref

一意のインデックスは、各インデックス キー クエリに対して、一致する一意の行データ (1 つだけ、それ以上、0 はありません) を返し、クエリ結果とデータの数は一貫している必要があります。

この状況は、ユニーク インデックスと主キー インデックスでよく見られます。

delete from teacher where tcid >= 4;
alter table teacherCard add constraint pk_tcid primary key(tcid);
alter table teacher add constraint uk_tcid unique index(tcid) ;
explain select t.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid ;

結果は次のとおりです。

要約: 上記の SQL では、使用されるインデックスは t.tcid であり、これは教師テーブルの tcid フィールドです; 教師テーブルのデータ数が接続クエリのデータ数と同じ場合 (両方とも 3データの部分)、eq_ref レベルを満たすことは可能ですが、それ以外の場合は満たすことはできません。条件は厳しく、達成するのは困難です。

④参照

一意でないインデックス、インデックス キー クエリごとに、一致するすべての行を返します (0 の場合も、1 の場合も、それ以上の場合もあります)。

データを準備します。

インデックスを作成し、実行計画を表示します。

# 添加索引
alter table teacher add index index_name (tname) ;
# 查看执行计划
explain select * from teacher     where tname = 'tz';

結果は次のとおりです。

⑤範囲

指定された範囲の行を取得します。その後に範囲クエリ (between、>、<、>=、in) が続きます。

in が失敗することがあり、インデックスがない場合は ALL になります

# 添加索引
alter table teacher add index tid_index (tid) ;
# 查看执行计划:以下写了一种等价SQL写法,查看执行计划
explain select t.* from teacher t where t.tid in (1,2) ;
explain select t.* from teacher t where t.tid <3 ;

結果は次のとおりです。

⑥インデックス

すべてのインデックスのデータをクエリ (インデックス全体をスキャン)

⑦全て

すべてのソース テーブルのデータをクエリする (ブルート フォース スキャンのフル テーブル)

注: cid はインデックス フィールドであるため、インデックス フィールドをクエリするには、インデックス テーブルをスキャンするだけで済みます。ただし、tid はインデックス フィールドではありません。インデックスのないフィールドをクエリするには、ソース テーブル全体をブルート フォース スキャンする必要があり、より多くのリソースを消費します。

4) possible_keys とキー

possible_keys 使用できるインデックス。それは予測です、確かではありません。ただ調べてください。

key は、使用される実際のインデックスを指します。

# 先给course表的cname字段,添加一个索引
create index cname_index on course(cname);
# 查看执行计划
explain select t.tname ,tc.tcdesc from teacher t,teacherCard tc
where t.tcid= tc.tcid
and t.tid = (select c.tid from course c where cname = 'sql') ;

結果は次のとおりです。

注意すべきことの 1 つは、 possible_key/key が NULL の場合、インデックスは役に立たないということです。

5) キーレン

複合インデックスが完全に使用されているかどうかを判断するために使用されるインデックスの長さ (a、b、c)。

①テスト用のテーブルを新規作成する

# 创建表
create table test_kl
(
    name char(20) not null default ''
);
# 添加索引
alter table test_kl add index index_name(name) ;
# 查看执行计划
explain select * from test_kl where name ='' ; 

結果は次のとおりです。

結果分析: サーバーの文字セットを設定していないため、デフォルトの文字セットは latin1 です. latin1 の場合、1 文字は 1 バイトを表すため、この列の key_len の長さは 20 であり、name のインデックスが使用されていることを示します.

②「not null」に設定されていないtest_klテーブルにname1カラムを追加

結果は次のとおりです。

結果分析: インデックス フィールドが null になる可能性がある場合、MySQL の最下層は識別に 1 バイトを使用します。

③元のインデックス名とname1を削除し、複合インデックスを追加

# 删除原来的索引name和name1
drop index index_name on test_kl ;
drop index index_name1 on test_kl ;
# 增加一个复合索引 
create index name_name1_index on test_kl(name,name1);
# 查看执行计划
explain select * from test_kl where name1 = '' ; --121
explain select * from test_kl where name = '' ; --60

結果は次のとおりです。

結果の分析: 以下の実行計画では、複合インデックスの最初のインデックス フィールド名のみを使用しているため、key_len は 20 であり、非常に明確です。上記の実行計画を見ると、複合インデックス フィールドの name1 フィールドのみを使用していますが、複合インデックスの 2 番目のインデックス フィールドを使用する場合は、デフォルトで複合インデックスの最初のインデックス フィールド名が使用されます。 . name1 は null になる可能性があるため、key_len = 20 + 20 + 1 = 41!

④ name2 フィールドを再度追加し、このフィールドのインデックスを作成する方法。

違いは、フィールドのデータ型が varchar であることです。

# 新增一个字段name2,name2可以为null
alter table test_kl add column name2 varchar(20) ; 
# 给name2字段,设置为索引字段
alter table test_kl add index name2_index(name2) ;
# 查看执行计划
explain select * from test_kl where name2 = '' ;  

結果は次のとおりです。

結果の分析: key_len = 20 + 1 + 2、この 20 + 1 はわかっています。この 2 は何を表しているのでしょうか? varchar は可変長であることがわかりました.mysql の最下層では、可変長を識別するために 2 バイトが使用されます。

6)参照

ここでの ref の役割は、現在のテーブルによって参照されるフィールドを示します。

type で ref 値と区別されることに注意してください。type では、ref は型 type の単なるオプション値です。

# 给course表的tid字段,添加一个索引
create index tid_index on course(tid);
# 查看执行计划
explain select * from course c,teacher t 
where c.tid = t.tid  
and t.tname = 'tw';

結果は次のとおりです。

結果の分析: 2 つのインデックスがあり、c テーブルの c.tid は t テーブルの tid フィールドを参照しているため、表示結果が [データベース名.t.tid] であることがわかります。 t テーブルの名前は定数「tw」を参照しているため、結果が定数を意味する const として表示されていることがわかります。

7) 行 (これはまだ少し疑わしいです)

インデックスの最適化によってクエリされたデータの数 (インデックスを通じて実際にクエリされたデータの数)

explain select * 
from course c,teacher t  
where c.tid = t.tid
and t.tname = 'tz' ;

結果は次のとおりです。

8)おまけ

他の指示を示すことも有用です。

①ファイルソートを使用:単一インデックスの場合

この単語が表示される場合、現在の SQL パフォーマンスが多く消費されていることを意味します。「余分な」ソートが実行されたことを示します。通常、order by ステートメントで使用されます。

Ⅰ 「エクストラ」ソートとは?

これを明確にするために、まずソートとは何かを知る必要があります。フィールドを並べ替える場合は、まずこのフィールドをクエリしてから、このフィールドを並べ替える必要があります。

次に、次の 2 つの SQL ステートメントの実行計画を見ていきます。

# 新建一张表,建表同时创建索引
create table test02
(
    a1 char(3),
    a2 char(3),
    a3 char(3),
    index idx_a1(a1),
    index idx_a2(a2),
    index idx_a3(a3)
);
# 查看执行计划
explain select * from test02 where a1 ='' order by a1 ;
explain select * from test02 where a1 ='' order by a2 ; 

結果は次のとおりです。

結果の分析: 最初の実行計画では、where の後、最初に a1 フィールドをクエリし、次に a1 を使用して順番に並べ替えます。これは非常に簡単です。ただし、2 番目の実行プランでは、where の後に a1 フィールドをクエリしますが、a2 フィールドは並べ替えに使用されます。

要約: 単一のインデックスの場合、並べ替えと検索が同じフィールドの場合、filesort の使用は発生しません; 並べ替えと検索が同じフィールドでない場合、filesort の使用は発生します; したがって、フィールドがどこにあるか、どのフィールドで並べ替えます。

②ファイルソートの使用:複合インデックスの場合

列をまたがることはできません (公式用語: ベストレフトプレフィックス)

# 删除test02的索引
drop index idx_a1 on test02;
drop index idx_a2 on test02;
drop index idx_a3 on test02;
# 创建一个复合索引
alter table test02 add index idx_a1_a2_a3 (a1,a2,a3) ;
# 查看下面SQL语句的执行计划
explain select *from test02 where a1='' order by a3 ;  --using filesort
explain select *from test02 where a2='' order by a3 ; --using filesort
explain select *from test02 where a1='' order by a2 ;

結果は次のとおりです。

結果の分析: 複合インデックスの順序は (a1, a2, a3) です. a1 が最も左側にあることがわかります. したがって, a1 は「ベストレフトプレフィックス」と呼ばれます. 次のインデックスを使用する場合フィールドの場合、最初にこの a1 フィールドを使用する必要があります。Explain1 では、where の後に a1 フィールドを使用しますが、後続の並べ替えでは a3 を使用します。これは、クロス列に属する a2 を直接スキップします。explain2 では、where の後に a2 フィールドを使用し、a1 フィールドを直接スキップし、さらに属します列を横断する; Explain3 では、where の後に a1 フィールドと a2 フィールドを使用するため、[ファイルソートを使用する] はありません。

③一時的に使う

この単語が表示される場合は、現在の SQL パフォーマンスが多く消費されていることも意味します。これは、現在の SQL が一時テーブルを使用しているためです。通常は group by に表示されます。

explain select a1 from test02 where a1 in ('1','2','3') group by a1 ;
explain select a1 from test02 where a1 in ('1','2','3') group by a2 ; --using temporary

結果は次のとおりです。

結果分析: どのフィールドをクエリすると、そのフィールドでグループ化されます。それ以外の場合は一時的な使用が表示されます。

一時的な使用について、例を見ていきます。

using temporary は、追加のテーブルを使用する必要があることを示します。これは通常、group by ステートメントに表示されます。すでにテーブルがありますが、適用できず、別のテーブルを作成する必要があります。

mysql の書き込み処理と解析処理をもう一度見てみましょう。

Ⅰ 執筆の流れ

select dinstinct  ..from  ..join ..on ..where ..group by ..having ..order by ..limit ..

Ⅱ 分析プロセス

from .. on.. join ..where ..group by ..having ..select dinstinct ..order by ..limit ..

明らかに、where の後に group by を指定してから選択します。これに基づいて、次の 2 つの SQL ステートメントの実行計画を見てみましょう。

explain select * from test03 where a2=2 and a4=4 group by a2,a4;
explain select * from test03 where a2=2 and a4=4 group by a3;

分析は次のとおりです: 最初の実行計画では、a2 と a4 が続き、次に a2 と a4 に従ってグループ化します.明らかに、これら 2 つのテーブルは既に存在しており、a2 と a4 で直接グループ化できます. しかし、2 番目の実行計画では、a2 と a4 が続き、次に a3 でグループ化します.明らかに、a3 テーブルがないため、別の一時テーブル a3 が必要です. したがって、一時的な使用があります。

④インデックスを使う

このキーワードが表示された場合は、SQL のパフォーマンスが向上したことを意味します。

インデックスを使用すると、「インデックス カバレッジ」と呼ばれます。

using index と表示されている場合は、ソース テーブルを読み取る必要はなく、インデックスを使用してデータを取得するだけで、ソース テーブルにクエリを返す必要がないことを意味します。

使用される列がすべてインデックスに表示される限り、それはインデックス カバレッジです。

# 删除test02中的复合索引idx_a1_a2_a3
drop index idx_a1_a2_a3 on test02;
# 重新创建一个复合索引idx_a1_a2
create index idx_a1_a2 on test02(a1,a2);
# 查看执行计划
explain select a1,a3 from test02 where a1='' or a3= '' ;
explain select a1,a2 from test02 where a1='' and a2= '' ;

結果は次のとおりです。

結果分析: a1 と a2 の複合インデックスを作成しました。最初の実行計画では、a3 があります。このフィールドはインデックスを作成しないため、インデックスを使用するのではなく、where を使用して、元に戻る必要があることを示します。テーブル。お問い合わせください。2 番目の実行計画では、完全なインデックス カバレッジであるため、using インデックスがあります。

インデックスを使用する場合、次のケースを検討しています。

explain select a1,a2 from test02 where a1='' or a2= '' ;
explain select a1,a2 from test02;

結果は次のとおりです。

インデックス カバレッジが使用されている場合 (インデックスを使用している場合)、possible_keys とキーに影響します。

a. where がない場合、インデックスはキーにのみ表示されます。

b. where がある場合、インデックスは key と possible_keys に表示されます。

⑤使用場所

[return table query] が必要であることを示します。これは、クエリがインデックスとソース テーブルの両方で実行されることを意味します。

# 删除test02中的复合索引idx_a1_a2
drop index idx_a1_a2 on test02;
# 将a1字段,新增为一个索引
create index a1_index on test02(a1);
# 查看执行计划
explain select a1,a3 from test02 where a1="" and a3="" ;

結果は次のとおりです。

結果の分析: 両方ともインデックス a1 を使用します。これは、インデックスを使用してクエリを実行することを意味します。ただし、a3 フィールドについてはインデックスを使用しなかったため、a3 フィールドについては、ソース テーブルにクエリを戻す必要があり、現時点では where を使用して表示されます。

⑥ありえないところ(了解)

where 句が常に False の場合に不可能な where が発生する

# 查看执行计划
explain select a1 from test02 where a1="a" and a1="b" ;

結果は次のとおりです。

6. 最適化の例

1) 事例紹介

# 创建新表
create table test03
(
    a1 int(4) not null,
    a2 int(4) not null,
    a3 int(4) not null,
    a4 int(4) not null
);
# 创建一个复合索引
create index a1_a2_a3_test03 on test03(a1,a2,a3);
# 查看执行计划
explain select a3 from test03 where a1=1 and a2=2 and a3=3;

結果は次のとおりです。

推奨される書き込み方法: 複合インデックスの順序は、使用順序と一致しています。

【おすすめしない書き方】を見てみましょう:複合インデックスの順番が使用順と矛盾しています。

# 查看执行计划
explain select a3 from test03 where a3=1 and a2=2 and a1=3;

結果は次のとおりです。

結果の分析: 結果は上記の結果と一致していますが、このように記述することはお勧めできません。しかし、なぜこのように書いても問題ないのでしょうか? これは、順序を調整した SQL オプティマイザの働きによるものです。

最後に追加すること: 複合インデックスの場合、複数の列にまたがって使用しないでください

# 查看执行计划
explain select a3 from test03 where a1=1 and a3=2 group by a3;

結果は次のとおりです。

結果分析: a1_a2_a3 は複合インデックスです.a1 インデックスを使用した後,列全体で a3 を直接使用し,インデックス a2 を直接スキップします.したがって,インデックス a3 は無効です.a3 をグループ化に使用すると,使用場所が表示されます.

2) 単一テーブルの最適化

# 创建新表
create table book
(
        bid int(4) primary key,
        name varchar(20) not null,
        authorid int(4) not null,
        publicid int(4) not null,
        typeid int(4) not null 
);
# 插入数据
insert into book values(1,'tjava',1,1,2) ;
insert into book values(2,'tc',2,1,2) ;
insert into book values(3,'wx',3,2,1) ;
insert into book values(4,'math',4,2,3) ;    

結果は次のとおりです。

ケース: authorid=1 および typeid 2 または 3 で入札をクエリし、typeid に従って降順で並べ替えます。

explain 
select bid from book 
where typeid in(2,3) and authorid=1  
order by typeid desc ;    

結果は次のとおりです。

これは最適化されていない SQL です.typ が ALL で、extra が filesort を使用していることがわかります.この SQL がどれほど恐ろしいものか想像できます.

最適化: インデックスを追加するときは、MySQL の解析順序に従ってインデックスを追加する必要があります.MySQL の解析順序に戻ります.MySQL の解析順序を見てみましょう.

from .. on.. join ..where ..group by ..having ..select dinstinct ..order by ..limit ..

①最適化1:これを踏まえてインデックスを追加し、再度実行計画を確認します。

# 添加索引
create index typeid_authorid_bid on book(typeid,authorid,bid);
# 再次查看执行计划
explain 
select bid from book 
where typeid in(2,3) and authorid=1  
order by typeid desc ;

結果は次のとおりです。

結果の分析: 結果は想像したとおりではありません。まだ使用場所があり、インデックスの長さ key_len=8 を確認します。これは、2 つのインデックスのみを使用し、1 つのインデックスが無効であることを意味します。

② 最適化2:in を使うとインデックスが失敗することがある これを踏まえて、以下の最適化案があります。

in フィールドを最後に置きます。1 つの注意点: 新しいインデックスが作成されるたびに、以前に放棄されたインデックスを削除することをお勧めします。そうしないと、(インデックス間で) 干渉が発生することがあります。

# 删除以前的索引
drop index typeid_authorid_bid on book;
# 再次创建索引
create index authorid_typeid_bid on book(authorid,typeid,bid);
# 再次查看执行计划
explain 
select bid from book 
where authorid=1  and typeid in(2,3)  
order by typeid desc ;

結果は次のとおりです。

結果の分析: ここに変更はありませんが、これは最適化のアイデアです。

次のように要約します。

a. インデックスの定義と使用において順序の一貫性を維持するために、プレフィックスを使用するのが最善です

b. インデックスは段階的に最適化する必要があります (新しいインデックスが作成されるたびに、状況に応じて以前に放棄されたインデックスを削除する必要があります)。

c. 無効化を防ぐために、where 条件の最後に In を含む範囲クエリを配置します。

この例では、 where を使用する (元のテーブルに戻る必要がある); index を使用する (元のテーブルに戻る必要はない): 理由、authorid=1 で typeid が (2,3) の場合、authorid はインデックスにある ( authorid、typeid、bid) であるため、元のテーブルに戻る必要はありません (インデックス テーブルで直接見つけることができます)。また、typeid はインデックス (authorid、typeid、bid) にもありますが、範囲クエリは含むと typeid インデックスが無効になるため、There is no index of typeid, so you need to go back to the original table (where を使用); と同等です。

たとえば、In がなければ、where を使用することはできません。

explain select bid from book 
where  authorid=1 and typeid =3
order by typeid desc ;

結果は次のとおりです。

3) 2 テーブル最適化

# 创建teacher2新表
create table teacher2
(
        tid int(4) primary key,
        cid int(4) not null
);
# 插入数据
insert into teacher2 values(1,2);
insert into teacher2 values(2,1);
insert into teacher2 values(3,3);
# 创建course2新表
create table course2
(
    cid int(4) ,
    cname varchar(20)
);
# 插入数据
insert into course2 values(1,'java');
insert into course2 values(2,'python');
insert into course2 values(3,'kotlin');

例: 左結合を使用して、Java クラスを教えるためのすべての情報を検索します。

explain 
select *
from teacher2 t 
left outer join course2 c
on t.cid=c.cid 
where c.cname='java';

結果は次のとおりです。

①最適化

2 つのテーブルの場合、インデックスはどこに追加する必要がありますか? A: テーブルの結合では、小さなテーブルが大きなテーブルを駆動します。インデックスは、頻繁に使用されるフィールドに基づいて作成されます。

小さなテーブルが大きなテーブルを駆動する方が良いのはなぜですか?

    小表:10
    大表:300
# 小表驱动大表
select ...where 小表.x10=大表.x300 ;
for(int i=0;i<小表.length10;i++)
{
    for(int j=0;j<大表.length300;j++)
    {
        ...
    }
}
# 大表驱动小表
select ...where 大表.x300=小表.x10 ;
for(int i=0;i<大表.length300;i++)
{
    for(int j=0;j<小表.length10;j++)
    {
        ...
    }
}

分析: 上記の 2 つの FOR ループは、最終的に 3000 回ループしますが、2 層ループの場合: 通常、小さなデータを含むループは外側の層に配置することをお勧めします。大規模なデータを含むループの場合は、内側のレイヤーに配置します。理由はどうあれ、これはプログラミング言語の原則であり、二重ループの場合、外側のループが少なく、メモリループが大きいほど、プログラムのパフォーマンスは高くなります。

結論: […on t.cid=c.cid] と書くときは、データ量の少ないテーブルを左側に置く (t テーブルのデータ量が少なく、c のデータ量が少ないと仮定)テーブルは大きいです。)

2 つのテーブルを接続する場合、t が小さなテーブル (10 エントリ) の場合、[…on t.cid=c.cid] のように、小さなテーブルを使用して大きなテーブルを駆動する必要があることは既にわかっています。 c は大きなテーブル (300 エントリ) であり、各サイクルで t を 300 回循環する必要があります。つまり、t テーブルの t.cid フィールドは頻繁に使用されるフィールドに属しているため、インデックスを追加する必要があります。 cid フィールド。

より詳細な説明: 一般に、左の結合は左のテーブルにインデックスを付けます。右側の結合は、右側のテーブルにインデックスを付けます。他のテーブルにインデックスを付ける必要があるかどうかにかかわらず、それらを段階的に試していきます。

# 给左表的字段加索引
create index cid_teacher2 on teacher2(cid);
# 查看执行计划
explain 
select *
from teacher2 t 
left outer join course2 c
on t.cid=c.cid 
where c.cname='java';

結果は次のとおりです。

もちろん、下に移動して最適化し、cname にインデックスを追加することもできます。インデックスの最適化は、少し実験が必要な段階的なプロセスです。

# 给cname的字段加索引
create index cname_course2 on course2(cname);
# 查看执行计划
explain 
select t.cid,c.cname
from teacher2 t 
left outer join course2 c
on t.cid=c.cid 
where c.cname='java';

結果は次のとおりです。

最後に、1 つ追加します。結合バッファーの使用は追加のオプションです。これは、Mysql エンジンが「接続キャッシュ」を使用することを意味します。つまり、MySQL の最下層が SQL を移動し、書き方が悪すぎます。

4) 3 テーブル最適化

  • テーブル以上、最適化の原則は同じ

  • 小さなテーブルが大きなテーブルを動かす

  • インデックスは、頻繁にクエリされるフィールドに基づいて構築されます

7. インデックスの失敗を回避するためのいくつかの原則

①複合指数の注意点

  • 複合インデックス、列全体または順不同で使用しない (左に最適なプレフィックス)

  • 複合インデックスの場合は、完全なインデックス マッチングを使用してみてください。つまり、複数のインデックスを作成する場合は、複数のインデックスを使用してください。

② インデックスに対して操作(計算、関数、型変換)を行わないでください。インデックスが無効になります。

explain select * from book where authorid = 1 and typeid = 2;
explain select * from book where authorid*2 = 1 and typeid = 2 ;

結果は次のとおりです。

③ インデックスは、等しくない (!= <>) または null (null でない) を使用できません。そうしないと、すべての右辺が無効になります (ほとんどの場合)。複合インデックスに > がある場合、自己および右のインデックスはすべて無効です。

# 针对不是复合索引的情况
explain select * from book where authorid != 1 and typeid =2 ;
explain select * from book where authorid != 1 and typeid !=2 ;

結果は次のとおりです。

このケースをもう一度見てください。

# 删除单独的索引
drop index authorid_index on book;
drop index typeid_index on book;
# 创建一个复合索引
alter table book add index idx_book_at (authorid,typeid);
# 查看执行计划
explain select * from book where authorid > 1 and typeid = 2 ;
explain select * from book where authorid = 1 and typeid > 2 ;

結果は次のとおりです。

結論: 複合インデックスに [>] がある場合、インデックス自体と正しいインデックスはすべて無効になります。

複合インデックスに [<] がある場合を見てください。

ほとんどの状況に当てはまる結論ですが、SQL オプティマイザーなどの理由により 100% 正しいわけではありません。一般に、範囲クエリ (>< in) の後、インデックスは無効になります。

④ SQL 最適化は確率レベルの最適化です。私たちの最適化が実際に使用されているかどうかについては、Explain によって推測する必要があります。

# 删除复合索引
drop index authorid_typeid_bid on book;
# 为authorid和typeid,分别创建索引
create index authorid_index on book(authorid);
create index typeid_index on book(typeid);
# 查看执行计划
explain select * from book where authorid = 1 and typeid =2 ;

結果は次のとおりです。

結果の分析: 2 つのインデックスを作成しましたが、実際に使用されたインデックスは 1 つだけでした。2 つの個別のインデックスの場合、プログラムは 1 つのインデックスだけで十分であり、2 つを使用する必要はないと判断するためです。

複合インデックスを作成するときは、上記の SQL を再度実行します。

# 查看执行计划
explain select * from book where authorid = 1 and typeid =2 ;

結果は次のとおりです。

⑤インデックスカバー率100%問題なし

⑥ 「定数」で始めようとするように、「%」で始めないでください。そうしないと、インデックスが無効になります

explain select * from teacher where tname like "%x%" ;
explain select * from teacher  where tname like 'x%';
explain select tname from teacher  where tname like '%x%';

結果は次のとおりです。

結論は次のとおりです。「%x%」のようなケースは使用しないようにしますが、「x%」のケースは使用できます。「%x%」を使用していない場合は、インデックス カバレッジを使用する必要があります。

⑦型変換(明示的、暗黙的)を使用しないようにしてください。そうしないと、インデックスが失敗します

explain select * from teacher where tname = 'abc' ;
explain select * from teacher where tname = 123 ;

結果は次のとおりです。

⑧使用しないようにしてください。そうしないと、インデックスが失敗します

explain select * from teacher where tname ='' and tcid >1 ;
explain select * from teacher where tname ='' or tcid >1 ;

結果は次のとおりです。

注: or は非常に暴力的で、それ自体のインデックスと左右のインデックスの両方が無効になります。

8. その他の最適化方法

1) exists と in の最適化

メイン クエリのデータ セットが大きい場合は、より効率的な i キーワードが使用されます。

サブクエリのデータ セットが大きい場合は、exist キーワードを使用するとより効率的です。

select ..from table where exist (子查询) ;
select ..from table where 字段 in  (子查询) ;

2) 最適化による順序

  • IO は、ハードディスク ファイルへのアクセス回数です。

  • filesort の使用には、双方向ソートと一方向ソート (IO の数による) の 2 つのアルゴリズムがあります。

  • MySQL 4.1 より前では、双方向の並べ替えがデフォルトで使用されていました; 双方向: ディスクを 2 回スキャンします (1: ディスクから並べ替えフィールドを読み取り、並べ替えフィールドを並べ替えます (バッファ内で並べ替えます) 2: 他のフィールドをスキャンします)。

  • MySQL 4.1 以降では、一方向の並べ替えがデフォルトで使用されます。つまり、一度だけ読み取り (すべてのフィールド)、バッファー内で並べ替えます。ただし、単一方向の並べ替えには、特定の隠れた危険があります (必ずしも「単一方向/1 IO」ではなく、複数の IO が存在する場合があります)。理由:特にデータ量が多い場合、全フィールドのデータを一度に読み込めないため、「分割読み込み、複数読み込み」を行います。

  • 注: 一方向の並べ替えは、双方向の並べ替えよりも多くのバッファーを占有します。

  • 一方向の並べ替えを使用する場合、データが大きい場合は、バッファーのサイズを大きくすることを検討できます。

# 不一定真的是“单路/1次IO”,有可能多次IO
set max_length_for_sort_data = 1024 

max_length_for_sort_data の値が小さすぎる場合、mysql は自動的に一方向 -> 双方向 (小さすぎる: 並べ替える必要がある列の合計サイズが max_length_for_sort_data で定義されたバイト数を超える) に移行します。

① クエリによるオーダーの戦略を改善する:

  • シングルチャンネルまたはデュアルチャンネルの使用を選択し、バッファの容量を調整します

  • select * ... の使用は避けてください (select の後にすべてのフィールドを書き込みます。これは * を書き込むよりも効率的です)

  • 複合インデックス、列全体で使用しない、ファイル ソートを使用しないですべてのソート フィールドを確保する、ソートの一貫性 (昇順または降順の両方)

おすすめ

転載: blog.csdn.net/ytp552200ytp/article/details/126122668