単純なmysqlクエリがありますが、レコードがたくさんある場合(現在、103,0000)、パフォーマンスが非常に遅く、ファイルソートを使用していると表示されます。これが遅い理由かどうかはわかりません。それをスピードアップするための提案はありますか?またはfilesortを使用して停止しますか?
MYSQLクエリ:
SELECT adverts .*
FROM adverts
WHERE (
price >='0'
)
AND (
adverts.status = 1
)
AND (
adverts.approved = 1
)
ORDER BY date_updated DESC
LIMIT 19990 , 10
説明の結果:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE adverts range price price 4 NULL 103854 Using where; Using filesort
広告テーブルとインデックスは次のとおりです。
CREATE TABLE `adverts` (
`advert_id` int(10) NOT NULL AUTO_INCREMENT,
`user_id` int(10) NOT NULL,
`type_id` tinyint(1) NOT NULL,
`breed_id` int(10) NOT NULL,
`advert_type` tinyint(1) NOT NULL,
`headline` varchar(50) NOT NULL,
`description` text NOT NULL,
`price` int(4) NOT NULL,
`postcode` varchar(7) NOT NULL,
`town` varchar(60) NOT NULL,
`county` varchar(60) NOT NULL,
`latitude` float NOT NULL,
`longitude` float NOT NULL,
`telephone1` varchar(15) NOT NULL,
`telephone2` varchar(15) NOT NULL,
`email` varchar(80) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0',
`approved` tinyint(1) NOT NULL DEFAULT '0',
`date_created` datetime NOT NULL,
`date_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expiry_date` datetime NOT NULL,
PRIMARY KEY (`advert_id`),
KEY `price` (`price`),
KEY `user` (`user_id`),
KEY `type_breed` (`type_id`,`breed_id`),
KEY `headline_keywords` (`headline`),
KEY `date_updated` (`date_updated`),
KEY `type_status_approved` (`advert_type`,`status`,`approved`)
) ENGINE=MyISAM AUTO_INCREMENT=103878 DEFAULT CHARSET=utf8
問題は、MySQLがクエリの実行時に1つのインデックスしか使用しないことです。 WHERE
句の3つのフィールドを使用する新しいインデックスを追加すると、行の検索が速くなります。
ALTER TABLE `adverts` ADD INDEX price_status_approved(`price`, `status`, `approved`);
MySQLのドキュメントによると ORDER BY最適化 :
場合によっては、MySQLはインデックスを使用してORDER BYを解決できませんが、WHERE句に一致する行を見つけるためにインデックスを使用します。これらのケースには、次のものが含まれます。
行のフェッチに使用されるキーは、ORDERBYで使用されるキーと同じではありません。
これはあなたの場合に起こることです。 EXPLAIN
の出力が示すように、オプティマイザーはキーprice
を使用して行を検索します。ただし、ORDER BY
はフィールドdate_updated
にあり、キーprice
に属していません。
行をより速く検索し、行をより速くソートするには、WHERE
およびORDER BY
句で使用されるすべてのフィールドを含むインデックスを追加する必要があります。
ALTER TABLE `adverts` ADD INDEX status_approved_date_updated(`status`, `approved`, `date_updated`);
並べ替えに使用するフィールドは、インデックスの最後の位置にある必要があります。クエリで使用される条件は値の範囲を返すため、インデックスにprice
を含めることは無意味です。
それでもEXPLAIN
がfilesortを使用していることを示している場合は、MySQLに選択したインデックスを使用させることができます。
SELECT adverts.*
FROM adverts
FORCE INDEX(status_approved_date_updated)
WHERE price >= 0
AND adverts.status = 1
AND adverts.approved = 1
ORDER BY date_updated DESC
LIMIT 19990, 10
MySQLオプティマイザはほとんどの場合正しい選択を行うため、通常はインデックスを強制する必要はありません。しかし、時にはそれは悪い選択をするか、最良の選択ではありません。パフォーマンスが向上するかどうかを確認するには、いくつかのテストを実行する必要があります。
'0'
の周りのチェックマークを削除します。現在、インデックスの使用が妨げられている可能性がありますが、よくわかりません。それでも、価格はint
タイプであり、文字列ではないため、より良いスタイルです。
SELECT adverts .*
FROM adverts
WHERE (
price >= 0
)
AND (
adverts.status = 1
)
AND (
adverts.approved = 1
)
ORDER BY date_updated DESC
LIMIT 19990 , 10
2つの提案があります。まず、where句のゼロ付近の引用符を削除します。その行は次のようになります。
price >= 0
次に、次のインデックスを作成します。
CREATE INDEX `helper` ON `adverts`(`status`,`approved`,`price`,`date_created`);
これにより、MySQLは、インデックスのみを使用して、LIMIT句で指定された10行を見つけることができます。 Filesort自体は悪いことではありません...処理する必要のある行数はです。
WHERE
条件はprice
、status
、approved
を使用して選択し、次にdate_updated
を使用して並べ替えます。
したがって、これらのフィールドを持つsingleインデックスが必要です。 approved
、status
、price
、date_updated
の順にインデックスを作成することをお勧めします。
一般的なルールは、WHEREの等式を最初に配置し、次に範囲(以上、以下、等しい、間など)を配置し、最後にフィールドを並べ替えることです。 (1つのフィールドを省略すると、この目的でインデックスが使用できなくなったり、使用できなくなったりする可能性があることに注意してください)。
CREATE INDEX advert_ndx ON adverts (approved, status, price, date_updated);
このように、テーブルデータへのアクセスは、LIMIT
がその魔法を働かせた後にのみ必要であり、少数のレコードのみを低速で取得します。
また、不要なインデックスを削除すると、INSERT
sとUPDATE
sが高速化されます。
MySQLはキーを使用しませんdate_updated
は並べ替えに使用しますが、price
句で使用されているようにWHERE
キーを使用します。インデックスヒントの使用を試みることができます。
http://dev.mysql.com/doc/refman/5.1/en/index-hints.html
次のようなものを追加します
USE KEY FOR ORDER BY (date_updated)