2011-09-29 23 views
10

は、私は次のMySQLのテーブル(簡体字)があります。MySQLでこのインデックスを削除するとクエリが100倍高速になるのはなぜですか?

CREATE TABLE `track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` varchar(256) NOT NULL, 
    `is_active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `is_active` (`is_active`, `id`) 
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8 

「is_active」カラムは、私がほとんどで無視する行をマークし、すべてではないが、私のクエリの。私はこのテーブルからチャンクを定期的に読み出すクエリをいくつか持っています。そのうちの1つは次のようになります。

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 

このクエリは実行に1分かかります。

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| 1 | SIMPLE  | t  | ref | PRIMARY,is_active | is_active | 1  | const | 3747543 | Using where | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 

ここで、is_activeインデックスを無視するようにMySQLに指示すると、クエリは即座に実行されます。今

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | PRIMARY  | PRIMARY | 4  | NULL | 1597518 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

、本当に奇妙なのは、私が「is_active」インデックスを使用してMySQLを強制すると、クエリが再び瞬時に起こるということです!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | is_active  |is_active| 5  | NULL | 1866730 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

この動作はわかりません。 'is_active'インデックスでは、行はis_active、idの順にソートされます。私はクエリの 'is_active'と 'id'の両方の列を使用するので、IDを見つけるためにツリーの周りにいくつかのホップを行うだけで、テーブルからタイトルを取得するためにこれらのIDを使用する必要があるようです。

何が起こっているのですか?

編集:私はやっているかについての詳細情報:

  • クエリキャッシュは、OPTIMIZE TABLEを実行
  • 無効になり、TABLEをANALYZEは
  • 6620372行が 'is_active' Trueに設定した効果がなかったです。 874,714行に 'is_active'がFalseに設定されています。
  • FORCE INDEX(is_active)を使用すると、クエリが高速化されます。
  • MySQLバージョン5.1.54
+2

ベンチマークの前にキャッシュをクリアしていますか? – dfb

+0

また、テーブルの統計情報が最新でインデックスが再構築されていることを確認してください。 (しかしこれはMySQLで行われます;-) –

+0

WHERE条件を逆にするとどうなりますか? 'どこで(track.id> 5580702とtrack.is_active = 1)' – EJP

答えて

7

MySQLがインデックスの使い方を誤っているようです。

このクエリプランでは、PRIMARYインデックスまたはis_activeインデックスのいずれかを使用していた可能性があり、track_is_activeによって最初に絞り込むためにis_activeが選択されています。ただし、索引の最初の列(track.is_active)のみを使用しています。それは3747543の結果を得て、それをフィルタリングしてソートする必要があります。

PRIMARYインデックスを選択した場合は、インデックスを使用して1597518行まで絞り込むことができ、それ以上の並べ替えを必要としないtrack.idの順に検索されます。それはより速くなります。

新情報:あなたはFORCE INDEXを使用している第三の場合

、MySQLはis_activeインデックスを使用しているが、代わりにのみ、最初の列を使用しての今、それは(key_lenに参照)両方の列を使用しています。したがって、is_activeで絞り込み、同じインデックスを使用してidでソートしてフィルタリングすることができます。また、is_activeは単一の定数であるため、ORDER BYは2番目の列で満たされます(つまり、インデックスの1つのブランチからの行は既にソートされた順序で)。これはPRIMARYを使用するよりも良い結果になると思われます。おそらく最初に意図したものでしょうか?

クエリが微妙な方法で変更されていない限り、このインデックスの両方の列をFORCE INDEXなしで使用していない理由はわかりません。そうでない場合は、MySQLに悪い決断を下してしまいます。あなたが試みることができる

+0

もちろん、MySQLよりも優れていると分かっているなら、いつでも[USE INDEX()](http://dev.mysql.com/doc/refman/5.5/en/index-hints.html)を使ってどのインデックスを提案することができます好むべきです。また、[ANALYZE TABLE](http://dev.mysql.com/doc/refman/5.5/en/analyze-table.html)を試してみてください。 – thomasrutter

+0

FORCE INDEX(is_active)を使用すると、クエリはすぐに実行されます(最近の編集を参照)。何か案は? – cwick

+0

私は確信していません - おそらく何らかのキャッシュ?たぶん、EXPLAIN出力を追加しますか?あなたは同じ出力を同じ順序で受け取りますか? – thomasrutter

1

私はスピードアップがあなたのwhere句が原因だと思います。私はそれが大きなテーブル全体の行の小さなサブセットを取得しているだけであると仮定しています。小さなサブセットでis_activeの検索されたデータのテーブルスキャンを実行する方が、大きなインデックスファイルでフィルタリングするよりも高速です。単一の列インデックスをトラバースすることは、複合インデックスをトラバースするよりもはるかに高速です。

関連する問題