2つのクエリがあります。
select some_other_column
from `table`
order by primary_index_column asc
limit 4000000, 10;
そして
select some_other_column
from `table`
order by secondary_index_column asc
limit 4000000, 10;
どちらも10行を返します。最初は2.74秒かかり、2番目は7.07秒かかります。 some_other_column
はインデックスの一部ではありません。 primary_index_column
は主キー列です。 secondary_index_column
には、Bツリーインデックスと200のカーディナリティ(MySQLによる)があります。
explain
の結果は次のとおりです。
mysql> explain select some_other_column from `table` order by primary_index_column limit 4000000, 10;
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+
| 1 | SIMPLE | table | index | NULL | PRIMARY | 4 | NULL | 4000010 | |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+
mysql> explain select some_other_column from `table` order by secondary_index_column limit 4000000, 10;
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
| 1 | SIMPLE | table | ALL | NULL | NULL | NULL | NULL | 4642945 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
MySQLが2番目のクエリに特定の実行プランを選択するのはなぜですか?最初のクエリにインデックスを使用でき、2番目のクエリにはインデックスを使用できない理由がわかりません。
InnoDBのインデックス付きカラムには、常に gen_clust_index(aka Clustered Index) への追加キーが付加されています。これは、インデックスの順序で行4000000に到達する最初のクエリによってトラバースされます。これは要求されている唯一の列であるため、テーブルへのアクセスは不要です。
2番目のクエリは、インデックスの付いていない列をテーブルからインデックス付きの列と共に一時テーブルに収集する必要があります。次に、一時テーブルがソートされてから、インデックス付けされていない列がSELECT出力として表示されます。
どちらの場合も、クエリは横断する行数を指定します。テーブルの行数は4636881であるため、フルスキャンが容易に期待できます。 MySQLクエリオプティマイザがフルスキャンを実行する場所を決定すると、対照が明らかになります。
計算を行うと、MySQLクエリオプティマイザが計算するものは次のとおりです。
私の正直な意見では、テーブルの行数、テーブルの現在のインデックス、およびクエリによって規定された行数により、MySQL Query Optimizerは正しい決定を行いました。
このインデックスを作成する
ALTER TABLE `table` ADD INDEX mynewndx (indexed_column,some_other_column);
そして、2番目のクエリは今後再びテーブルにアクセスすることはありません。 MySQL Query Optimizerは、この新しいインデックスを検出すると、まったく異なる動作をします。
order by
クエリの最適化に関するMySQLのドキュメント によれば、
場合によっては、MySQLがインデックスを使用してORDER BY [...]を解決できない場合があります。これらのケースには次のものが含まれます。
- [...]
- 使用されるテーブルインデックスのタイプは、行を順番に格納しません。たとえば、これはMEMORYテーブルのHASHインデックスに当てはまります。
InnoDBについての私の理解は、行は主キーに従って順番に格納されるということです。したがって、セカンダリインデックスの場合は順序が狂っています。