2011-11-15 6 views
5

order by節なしで 'select * from lookup'を実行すると、Mysqlオプティマイザがセカンダリインデックスを選択するのはなぜですか?MySQL Query Optimizerは、クラスタ化されたプライマリインデックスに対してセカンダリインデックスを選択するのはなぜですか?

プライマリキーよりも重要なセカンダリインデックスを追加したことを前提としているため、これはちょっとばかりか、これはシーンの最適化の裏側です。

すべてのリーフノードのスキャンがこのクエリに答えるために必要なすべてのデータを提供できるため、主キーで結果を並べ替えることが期待されます。これはどこである(ランダムな非アルファベット順

insert into lookup values(1, "Zebra"),(2, "Aardvark"),(3, "Fish"),(4,"Dog"),(5,"Cat"),(6,"Mouse"); 

クエリでデータをいくつかのデータを挿入し

create table lookup (
id int not null, 
primary key (id), 
name varchar(25), 
unique k_name (name) 
) engine=innodb; 

(AUTO_INCREMENTない注意してください)私は、単純なキー/値ペアのテーブルを作成し再現する

I)は、データが主キーの順序で返されることなど

mysql> select * from lookup; 
+----+----------+ 
| id | name  | 
+----+----------+ 
| 2 | Aardvark | 
| 5 | Cat  | 
| 4 | Dog  | 
| 3 | Fish  | 
| 6 | Mouse | 
| 1 | Zebra | 
+----+----------+ 
6 rows in set (0.00 sec) 

どこに期待しますそうではありません - k_nameリーフノードのスキャンが完了したようです。ここに表示

mysql> explain select * from lookup; 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
| 1 | SIMPLE  | lookup | index | NULL   | k_name | 28  | NULL | 6 | Using index | 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

私にはこれは、Mysqlがデータを返すためのカバレッジインデックスとしてk_nameを使用していると言います。 k_nameインデックスを削除すると、データはプライマリキーの順序で返されます。別の索引付けされていない列を追加すると、データは主キー順で返されます。

私の設定に関するいくつかの基本情報。

mysql> show table status like 'lookup'\G 
*************************** 1. row *************************** 
      Name: lookup 
     Engine: InnoDB 
     Version: 10 
    Row_format: Compact 
      Rows: 6 
Avg_row_length: 2730 
    Data_length: 16384 
Max_data_length: 0 
    Index_length: 16384 
     Data_free: 0 
Auto_increment: NULL 
    Create_time: 2011-11-15 10:42:35 
    Update_time: NULL 
    Check_time: NULL 
     Collation: latin1_swedish_ci 
     Checksum: NULL 
Create_options: 
     Comment: 
1 row in set (0.00 sec) 

mysql> select version(); 
+------------+ 
| version() | 
+------------+ 
| 5.5.15-log | 
+------------+ 
1 row in set (0.00 sec) 

答えて

4

実際には、クラスタード・インデックス(別名gen_clust_index)は、韻や順序がROWID以外の順序で入力されます。 idの順序でROWIDを順序付けることは事実上不可能です。

InnoDBでは、非クラスタ化インデックス(セカンダリインデックスとも呼ばれる)のレコードには、セカンダリインデックスにない行のプライマリキー列が含まれています。 InnoDBはこのプライマリキー値を使用して、クラスタ化インデックスの行を検索します。

セカンダリインデックスは順序を制御します。ただし、各セカンダリ索引エントリには、正しい行への主キーエントリがあります。また、k_nameについて述べたカバリング指数のシナリオを考えてみてください。

それでは、しばらくのギアを切り替えてPRIMARY KEYとk_nameをdiscusssてみましょう:

QUESTION:誰のあなたの元のクエリ、主キーまたはk_nameによって要求された多くの列を持っていますか?

ANSWER:k_nameは名前とIDの両方を持つため(PRIMARY KEYであるためIDは内部であるため)カバリングインデックスk_nameは、主キーよりも優れたクエリを実行します。クエリがSELECT * FROM ORDER BY idた場合

今、あなたのEXPLAIN PLANは次のようになります。

mysql> explain select * from lookup order by id; 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 
| 1 | SIMPLE  | lookup | index | NULL   | PRIMARY | 4  | NULL | 6 |  | 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 

1 row in set (0.00 sec) 

順序をspecfiyingがなければ、MySQLのクエリオプティマイザは、最良のあなたのクエリを満たしたインデックスが選択されます。

  • は、テーブルのすべての列がテーブルに
  • すべての列を個別にインデックスが作成されているのでもちろん、k_nameは、それが候補であるのでCandidate Key
  • k_nameは、セカンダリインデックスされていないされて不当な優位性を持っていますキーはPRIMARY KEYと同じです。
  • ユーザ定義クラスタ化インデックスは、一度変更された行の順序はあなたがですべての行の順序を操作することができない

を確立することはできません。

mysql> alter table lookup order by name; 
Query OK, 6 rows affected, 1 warning (0.23 sec) 
Records: 6 Duplicates: 0 Warnings: 1 

mysql> show warnings; 
+---------+------+-----------------------------------------------------------------------------------+ 
| Level | Code | Message                   | 
+---------+------+-----------------------------------------------------------------------------------+ 
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' | 
+---------+------+-----------------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 

mysql> alter table lookup order by id; 
Query OK, 6 rows affected, 1 warning (0.19 sec) 
Records: 6 Duplicates: 0 Warnings: 1 

mysql> show warnings; 
+---------+------+-----------------------------------------------------------------------------------+ 
| Level | Code | Message                   | 
+---------+------+-----------------------------------------------------------------------------------+ 
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' | 
+---------+------+-----------------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 
+2

k_nameは2次インデックスです。候補キーの定義はインデックスとは関係ありません。定義上の主キーでさえ、インデックスとは関係ありません。それにもかかわらず、ほとんどのDBMSは主キーを定義するときに自動的にインデックスを作成します。 –

+0

k_nameは二次索引ではありません。それはユニークキーであるため、PRIMARY KEYとまったく同じように動作します。一意のキーは、実際には、ユーザー定義のクラスタード・インデックスです。 PRIMARY KEYと同じくらいクラスタ化されたインデックスです。主キーが存在しない場合でも、ROWIDベースのgen_clust_indexが内部的に生成されます。このことの証明はInnoDBにあります。あなたは一日中、MyISAMに対してORDER BYの主キー列を実行できます。 InnoDBでは、UNIQUE KEYSを並べ替えることも、他のUNIQUE KEYSよりもPRIMARY KEYを優先してORDER-SELECT SELECTクエリを軽減することもできません。セカンダリインデックスは一意ではありません。 – RolandoMySQLDBA

+1

クラスタ化インデックスは、PRIMARYキーのインデックスです。 MySQLは、プライマリキーがない場合は、UNIQUE以外のインデックスのみをクラスタ化インデックスとして使用します。さらに、k_nameのような2次索引だけにも、対応する行を参照するための主キー・データが含まれています。別の列を追加すると、k_nameはクラスタードードのようにすべてのデータを提供できないことがわかります。 http://dev.mysql.com/doc/innodb/1.1/en/glossary.html#glos_secondary_index k_nameは、主キーが削除されたときにクラスタ化インデックスになります。 –

1

まあ、インデックスはそのクエリのデータを取得するという点でも同様に効率的であるので、私はちょうど

を「これはやる」別の一意のインデックスを追加して中退オプティマイザを推測しているのいずれかすべてが同等に効率的である可能性があります。一部の「FindBestIndex」ルーチンは、読み込まれた最後のものと一緒に脱落します。

私が注文を気にしていたとしても、私はidで注文を追加して、オプティマイザに2回通して並べ替えをせずにプライマリキーを選択させることを期待していました。

1

これは、InnoDBの2次インデックスにも主キー列が含まれているためです。したがって、MySQLはデータ行に触れることなくセカンダリインデックスから直接関連するすべてのデータをフェッチすることができるため、ディスクIOを節約しています。

参考文献:

+0

あなたが提供したリンクは、私の答えの生の例よりもテーブルとクエリを設計する前に知っておくべき概念を説明しています。 +1! – RolandoMySQLDBA

0

私はあなたがタイプ列を理解していなかったと思います。タイプ列 'index'は完全なインデックススキャンを意味します。この場合、 'extra'カラムに 'use index'があると、mysqlはクエリに必要なすべてのデータをindexから取得でき、実際のテーブル行に頼る必要はありません。そこでここでエンジンは行に行くのではなく(通常コストがかかります)、クエリで必要なすべてのデータを持つインデックスを使用します。セカンダリインデックスは、データとしてプライマリキー(あなたの場合はid)を持っています。つまり、セカンダリインデックスのキーを検索すると、テーブルレコードの主キーが取得されます。すべての値を尋ねただけなので、必要なものを得るためにセカンダリインデックスを反復するだけで十分です。

エンジンが主キーを反復することを選択した場合、主キーは実際の表の行に直接つながります。 Mysqlは、通常は非効率であるため、その動作を回避しようとします。通常、行には索引に含まれるデータよりも多くのデータが含まれているため、IOをさらに実行する必要があるため、非効率です。

http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

関連する問題