@[toc] 先ほど友達と共有したインデックス関連のコンテンツは、基本的に where 句でインデックスを使用しています。実は、インデックスにはもう 1 つの大きな用途があります。それは、ソートでのインデックスの使用です。今日はこれについて話しましょうトピック。
1. 2つの並べ替え方法
MySQL でクエリ結果を並べ替えたい場合は、order by を使用するだけです。SQL は非常に単純です。一般に、基礎となる実装には 2 つの異なるアイデアがあります。
- ファイルソート、ファイルソートとも呼ばれます。この名前は時々誤解を与えます。人々はディスク上でソートされていると考えていますが、実際には必ずしもそうではありません。データ量が比較的少ない場合、メモリ内で直接ソートされます。で十分であり、ディスク ファイルはメモリ内で並べ替えを実行できない場合にのみ使用されます。
- インデックスのソート。InnoDB のインデックスは B+Tree の形式でデータをまとめており、B+Tree 内のデータは順序付けされているため、インデックスをうまく活用できればソートはより効果的になります。
これら 2 つの並べ替え方法に関する限り、私の友人たちは、インデックスの設計が合理的で、最終的に 2 番目の方法に従って並べ替えることができるのであれば、それが最善であるに違いないことを発見しました。
ただし、ここで細かい点に注意する必要があり、2 番目のソート方法はテーブルに戻る必要がないことが前提となっており、クエリ処理中にテーブルに戻る必要がある場合、2 番目の方法はテーブルに戻る必要はありません。必然的に速い。理由も簡単です。
- テーブルに戻る必要がない場合、つまり、クエリしたいデータがすべてインデックス ツリー上にあり、インデックス ツリー自体のデータが順番に格納されている場合は、見つかったデータを直接返すことができます。それ自体は秩序あるものです。
- クエリ時にインデックス ツリーに必要なフィールドがない場合は、テーブルに戻る必要があります。友人は、テーブルに戻ると主キーの値が変わるため、テーブルへの戻りは基本的にランダム IO であることを知っています。必ずしも連続的ではない場合、効率は低くなります。したがって、現時点では、2 番目の並べ替え方法のパフォーマンスは、最初の並べ替え方法よりも強力ではない可能性があります。もちろん、決まった結論はなく、特定の状況と組み合わせて分析する必要があります。ここでは、友人に次のように伝えます。さまざまな状況が考えられます。
2. インデックスのソート
インデックスソートを使用したい場合、どのような条件を満たす必要がありますか?
前回の記事のデータを例として、次のテーブル構造があると仮定します。
CREATE TABLE `user` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`age` int DEFAULT NULL,
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`gender` varchar(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_prop_index` (`username`,`age`,`address`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
このテーブルには共同インデックスがあり、共同インデックスのフィールドにはユーザー名、年齢、住所が含まれます。
表内のデータは次のとおりです。
id (主キー) | ユーザー名 | 年 | 住所 | 性別 |
---|---|---|---|---|
1 | 腹筋 | 99 | 深セン | 男 |
2 | モノクロ | 95 | 天津 | 男 |
3 | CX | 93 | 深セン | 男 |
4 | 紀元前 | 80 | 上海 | 女性 |
5 | バックグラウンド | 85 | 重慶 | 女性 |
6 | 交流 | 98 | 広州 | 男 |
7 | モノクロ | 99 | 海口 | 女性 |
8 | ああ | 90 | 深セン | 男 |
9 | cc | 92 | 武漢 | 男 |
10 | の | 88 | 北京 | 女性 |
ユーザー名、年齢、住所の 3 つのフィールドが結合インデックスを形成すると想定されており、B+ツリーは次のようになります。
皆さん、ちょっと考えてみてください。どのようにクエリを実行すれば、結果は順番通りに得られるでしょうか?
全員が 1 分で要約します。
整理しましょう: インデックスの順序が order by 句の順序とまったく同じで、すべてのカラムのソート方向も同じである場合にのみ、MySQL はインデックスを介して結果をソートできます。結合インデックスの場合、order by 句も左端の一致原則を満たす必要があります。
いくつかの例を示します。
2.1 ケース 1
まず次の SQL を見てください。
select address from user order by username;
これは、ユーザー名でソートされたアドレス フィールドをクエリします。明らかに、必要なアドレス フィールドはこの結合インデックスの B+ ツリーに存在し、この結合インデックスの B+ ツリーはユーザー名に従って昇順で並べ替えられるため、次のようにこの SQL をインデックスで並べ替えることができます。形:
type:index
これは、MySQL がソートにインデックス スキャンを使用していることを示しています。
2.2 ケース2
次の SQL を見てみましょう。
select address from user order by username asc,age desc\G
この SQL は依然として、ユーザー名と年齢に従ってソートされているアドレス フィールドをクエリします。ユーザー名は昇順にソートされ、年齢は逆順にソートされます。皆さん、考えてみてください。前の結合インデックスの B+Tree では、ユーザー名が昇順 問題ありません、ユーザー名が同じ場合、年齢も昇順にソートされますが、SQL では昇順と降順が 1 つずつ必要です。当然、インデックス ツリーから取得したデータはそのような条件を満たすことができません。したがって、このクエリは、次に示すように、インデックス Sort を使用しません。
Extra のものは、 Using filesort
ここでファイルの並べ替えが必要であること、およびインデックス 並べ替えでは要件を満たすことができないことを示しています。
2.3 ケース 3
次の SQL を見てみましょう。
select address from user order by username desc
2.1 節の SQL と比較すると、この SQL のソート順が変更されており、最初の SQL は書き込み順序がなく、デフォルトは昇順ですが、この SQL は逆順に書かれています。B+Tree のユーザー名は昇順になっていますが、これをインデックスの並べ替えに使用できますか? これはインデックスのソートに使用できます。MySQL5.7 では、実行計画は次のようになります。
MySQL8.x では、実行計画は次のとおりです。
ご覧のとおり、違いは Extra にもう 1 つあることです Backward index scan
。
これは何を意味するのでしょうか?
MySQL8 より前では、インデックスを逆方向にスキャンできますが、逆方向スキャンの効率は低くなります。そのため、友人は MySQL5.7 でインデックスのソートが使用されていることがわかります。他には何も言われません。これは実際には逆方向にスキャンされたインデックスです。 。
MySQL8 以降、インデックス定義内の降順キーワード DESC は無視されなくなり、データを保存するときにインデックス ツリーを降順で保存できるため、将来クエリを実行するときにインデックスを順方向にスキャンできます。順方向スキャンの効率 逆方向スキャンに比べて効率が高くなります。
問題を説明するために例を挙げてみましょう。次のようなテーブルを作成する SQL があるとします。
CREATE TABLE t (
c1 INT, c2 INT,
INDEX idx1 (c1 ASC, c2 ASC),
INDEX idx2 (c1 ASC, c2 DESC),
INDEX idx3 (c1 DESC, c2 ASC),
INDEX idx4 (c1 DESC, c2 DESC)
);
MySQL5.7で上記のSQLを実行した後、テーブルの定義を見ると以下のような結果になりました。
実行中にインデックス フィールドの順序を設定しましたが、この順序は実際には無視されていることがわかります。
MySQL8 での実行後の結果を見てみましょう。
MySQL8 では、インデックスが定義されたときのフィールドの順序が保持されていることがわかります。これは、問題がないという先ほど述べたことが裏付けられます。
最後に、質問に戻りますが、Backward index scan
オプティマイザはクエリ時に降順インデックスを使用できるようになります。
2.4 ケース 4
次の SQL を見てみましょう。
select gender from user where username='ab' order by age
このSQLではユーザー名に特定の値を指定していますが、先ほどのB+Treeではユーザー名が決まったら次は年齢順にソートしていますが、年齢が同じ場合は住所順にソートしているので、上記の SQL はインデックスで並べ替え可能です。
2.5 ケース 5
次の SQL を見てみましょう。
select gender from user where username='ab' order by address
この SQL では、ユーザー名にも特定の値が割り当てられていますが、並べ替えは住所に基づいています。友人は、ユーザー名が決定されると、最初に年齢で並べ替えられ、次に住所で並べ替えられることを知っています。したがって、上記の SQL では、インデックス ツリーから読み取られたデータは必ずしもアドレスに従って配置されているわけではないため、上記の SQL はインデックスの並べ替えには使用できません。
2.6 ケース6
次の SQL を見てみましょう。
select gender from user where username like 'a%' order by age
この SQL のクエリ条件 username は範囲検索です。username が範囲検索の場合、対応する年齢が正しい順序であることは保証できないため、この SQL ではインデックス ソートを使用できません。
また、クエリ条件内の IN や BETWEEN などのキーワードも範囲検索とみなされ、これらのキーワードが where 句に含まれる場合、インデックス ソートが使用できない可能性があることに注意してください。
2.7 事例 7
次の SQL を見てみましょう。
select gender from user where username like 'a%' order by username,age
ユーザー名も範囲に従って検索されますが、最終的にソートされるときは、ユーザー名と年齢に従ってソートされます。範囲検索に従って取得されたユーザー名と年齢は順番になっているため、ここでもインデックスソートを使用できます。
2.8 ケース 8
次の SQL を見てみましょう。
select gender from user where username like 'a%' order by username,gender
この SQL について言うまでもなく、ソート フィールドにインデックス以外の列がある場合、インデックスを使用してソートすることは間違いなく不可能です。
つまり、where 句の条件に従って B+Tree からデータを検索した後、検索されたデータは正しい順序になっていますか? 順序付けされており、SQL で必要な順序である場合は、インデックス ソートを使用できますが、それ以外の場合は使用できません。
さて、冒頭の結論に戻って見てみましょう。この時点では誰もが簡単に理解できるはずです。
インデックスの順序が order by 句の順序とまったく同じで、すべてのカラムのソート方向も一貫している場合にのみ、MySQL はインデックスを介して結果をソートできます。 、order by 句も左端の一致原則を満たす必要があります。
3. その他の状況
3.1 複数テーブルの結合クエリ
複数テーブル結合クエリを使用してクエリを実行する場合、並べ替えが使用される場合、order by 句に含まれるフィールドはすべて最初のテーブルに存在する必要があり、この時点でインデックス 並べ替えが使用されます。
Song Ge 氏は、TienChin プロジェクトで例を挙げました。TienChin には、アクティビティ チャネル テーブル Tienchin_channel と、アクティビティ テーブル Tienchin_activity があります。アクティビティ テーブルは、チャネル テーブルの ID を参照します。次の複数テーブル結合クエリを実行してみましょう:
select ta.name from tienchin_activity ta inner join tienchin_channel tc using(`channel_id`)
この SQL の実行計画を見てみましょう。
このクエリでは、オプティマイザは ta テーブルを最初のテーブルとして使用し、tc テーブルを 2 番目のテーブルとして使用していることがわかります。前の結論によると、最初のテーブルのインデックスが並べ替えに使用される場合は、When が使用されます。インデックスソートの場合、2番目のテーブルは使用できませんので、確認してみましょう。
最初のテーブルのインデックスの場合はインデックス ソートが使用され、2 番目のテーブルのインデックスの場合はインデックス ソートが使用されず、両方のテーブルのインデックスが使用されている場合はインデックスが使用されないことがわかります。ソートして使用します。
3.2 null による順序付け
特別な状況もありますが order by null
、誰かがこのように書いているのを見たことのある友達はいますか?
MySQL8 より前では、デフォルトで group by フィールドに従ってソートされますが、この時点では、 order by null
これを追加して、MySQL にソートする必要がなく、結果を直接返すだけであることを伝えます。ファイルソートによる並べ替えが実行され、クエリの効率が低下する可能性 order by null
があります。
ただし、MySQL8 からは、デフォルトで group by フィールドに従ってソートされないため、この文は省略できるようになりました。
4. まとめ
さて、私は MySQL のインデックス ソートについて友達とたくさん話してきました。皆さんも何かを得られたことを願っています~