web-dev-qa-db-ja.com

MySQLでLIMITを使用すると、ORDER BY indexedColumnが途方もなく遅い

MyISAMテーブルには4.5m行があります。 LIMITのみのクエリは高速に戻ります。 ORDER BYのみのクエリは高速に戻ります。両方を組み合わせると終了しません。 USE INDEX(indexedColumn、Latitude、Longitude)も試しましたが、役に立ちません。

SELECT * FROM stuff WHERE (Latitude BETWEEN '29.187190582784076' AND '29.761053992852936' AND Longitude BETWEEN '-101.0597705588786' AND '-99.7414111838786') LIMIT 100;

89行; 0.71秒で戻る

SELECT * FROM stuff WHERE (Latitude BETWEEN '29.187190582784076' AND '29.761053992852936' AND Longitude BETWEEN '-101.0597705588786' AND '-99.7414111838786') ORDER BY indexedColumn DESC;

89行、0.84秒で戻ります

SELECT * FROM stuff WHERE (Latitude BETWEEN '29.187190582784076' AND '29.761053992852936' AND Longitude BETWEEN '-101.0597705588786' AND '-99.7414111838786') ORDER BY indexedColumn DESC LIMIT 100;

数分後に戻っていない

LIMITを使用したEXPLAIN ORDER BY

+----+-------------+-------+-------+--------------------------+--------------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys            | key          | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+--------------------------+--------------+---------+------+------+-------------+
|  1 | SIMPLE      | Stuff | index | Latitude,Longitude       | indexedColumn| 5       | NULL | 9643 | Using where |
+----+-------------+-------+-------+--------------------------+--------------+---------+------+------+-------------+

制限のみ

+----+-------------+-------+-------+--------------------+----------+---------+------+-------+-----------------------------------------------+
| id | select_type | table | type  | possible_keys      | key      | key_len | ref  | rows  | Extra                                         |
+----+-------------+-------+-------+--------------------+----------+---------+------+-------+-----------------------------------------------+
|  1 | SIMPLE      | stuff | range | Latitude,Longitude | Latitude | 30      | NULL | 81158 | Using index condition; Using where; Using MRR |
+----+-------------+-------+-------+--------------------+----------+---------+------+-------+-----------------------------------------------+

注文のみ

+----+-------------+-------+-------+--------------------+----------+---------+------+-------+---------------------------------------------------------------+
| id | select_type | table | type  | possible_keys      | key      | key_len | ref  | rows  | Extra                                                         |
+----+-------------+-------+-------+--------------------+----------+---------+------+-------+---------------------------------------------------------------+
|  1 | SIMPLE      | stuff | range | Latitude,Longitude | Latitude | 30      | NULL | 81158 | Using index condition; Using where; Using MRR; Using filesort |
+----+-------------+-------+-------+--------------------+----------+---------+------+-------+---------------------------------------------------------------+

全文検索にはMyISAMが必要ですが、AWS RDSには5.6.4がありません。 InnoDBではこれを試していません。

MariaDBサーバーでもこれを試しましたが、結果は同じです。サーバーのメモリサイズを増やしたのですが、役に立ちませんでした。

また、lat/lon doubleの代わりに空間ジオメトリを使用してみましたが、結果は同じでした。

これも試しました:

SELECT * FROM stuff ORDER BY indexedColumn DESC LIMIT 100

100行; 0.06秒

4 GBのメモリを備えたAWS RDSを使用し、key_buffer_sizeを2 GBに増やしました。テーブルのインデックスは600MBです。 RDSは主にInnoDB用に構成されているため、メモリを利用していないことに気づいていない他の構成がある可能性があります。

-------------編集---------------------

データを含む8 GBのAWS RDS MySQLインスタンスを作成しました。 1.1秒で戻る空間インデックスポイントでST_CONTAINSを使用しました(これは私のために機能します)。

他のクエリに問題がある理由を知りたいと思っています。 RAMが限られている構成の問題だと思います。私はこのような何百ものクエリを実行し、このような問題に遭遇したことはありません。ランダムなまぐれのようです。

MyISAMテーブルをInnoDBに変換して、重要かどうかを確認します。

3
Caleb Pitman

EXPLAINからは何が起こるかは非常に明確に見えます。「制限のみ」の方は、緯度にインデックスを使用します。これは、最も有用であることがわかったため、経度を求めてすべての行を試し、100行収集して終了するまでです。

「順序のみ」は同じパスを使用しますが、すべてを必要とするため100の後で停止しません-次に、一致するすべての行をファイルソートでソートします(ソートが高速になるように、81kのごく一部のみが返されると思います)。 。

しかし、遅い(間違って) "order by .. limit"最適化 を使用します==-何らかの理由で、多くの行が条件を満たしていると考えられるため、indexedColumnの順序で処理されます。しかし、実際には一致するものが少なすぎるため、一致する100行を見つける前にテーブルの大部分を調べます。つまり、使用可能なメモリがそれを格納するのに十分な大きさでない場合(または以前にロードされなかった場合)は、テーブルの大部分をディスクからメモリに読み取る必要があります。

結果-実際にはindexedColumnでインデックスを使用する必要はありません。一致する行が非常に少ないため、「遅い方法」でそれらをソートする方が実際にはインデックスを使用するよりも効果的です。これはignore index(indexedColumn)で実行できます。また、おそらく両方の(Latitude, Longitude)に複合インデックスを追加できます。これは数百行しか返さないはずであり、それらに対するすべての操作は簡単になります。

3
jkavalik

クエリを次のように書き換えます。

SELECT * 
FROM stuff FORCE INDEX(indexedColumn)
WHERE 
    (
        Latitude > '29.187190582784076' 
        AND Latitude < '29.761053992852936'
    ) 
    AND 
    (
        Longitude > '-101.0597705588786' 
        AND Longitude < '-99.7414111838786'
    ) 
ORDER BY DESC LIMIT 100;

FORCE INDEXオプションを試して、ネストされた大なり小なり以下の冗長クエリを試して、結果を確認してください。

0
jamescampbell