railsアプリケーションでgeokit(acts_as_mappable)を使用しています。多数のモデルがある場合、radialまたはbounds検索のパフォーマンスが大幅に低下します(私は1〜2百万回試しましたが問題は間違いありません)これより)。mysqlはインデックスを使用しているかどうか、geokitのパフォーマンスを改善できますか?
Geokitは、表の緯度と経度(緯度と経度)に基づいてすべての計算を行います。パフォーマンスを向上させるために、通常、緯度と経度の結合インデックスを使用してパフォーマンスを向上させることを目的として、バウンディングボックスの 'where'句を追加します。しかし、モデルの数が多いとまだそれは信じられないほど遅いですし、バウンディングボックスの節がそれよりもはるかに役立つはずです。
私の質問は、mysqlがlat/lngインデックスの組み合わせをうまく利用する方法、あるいはgeokit SQLクエリのパフォーマンスを向上させる方法ですか?または、緯度/経度の組み合わせインデックスをさらに役立てることはできますか?
編集:私は今、レールにこの作業を持って、より詳細に解決策を書いたhere背景例えば
、このクエリが与えられたの10マイル内のすべての場所を見つけ
詳細ポイント。 (私は.lengthを追加していくつの結果が返ってきたかを判断しました。これはgeokitでこれを言うより良い方法がありますが、より典型的なSQLクエリを強制したい)
Place.find(:all,:origin=>latlng,:within=>10).length
Mac miniでは約14秒かかります。ここでの結果での場所の数は1135である(とバウンディングボックス内の実際の桁数がちょうど1323年である)にもかかわらずだから、mysqlは87554行を検討している計画
mysql> explain SELECT *, (ACOS(least(1,COS(0.898529183781244)*COS(-0.0157233221653665)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+ -> COS(0.898529183781244)*SIN(-0.0157233221653665)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+ -> SIN(0.898529183781244)*SIN(RADIANS(places.lat))))*3963.19)
-> AS distance FROM `places` WHERE (((places.lat>51.3373601471464 AND places.lat<51.6264998528536 AND places.lng>-1.13302245886176 AND places.lng<-0.668737541138245)) AND ((ACOS(least(1,COS(0.898529183781244)*COS(-0.0157233221653665)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+
-> COS(0.898529183781244)*SIN(-0.0157233221653665)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+
-> SIN(0.898529183781244)*SIN(RADIANS(places.lat))))*3963.19)
-> <= 10))
-> ;
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | places | range | index_places_on_lat_and_lng | index_places_on_lat_and_lng | 10 | NULL | 87554 | 100.00 | Using where |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
を説明があります。
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
| places | 1 | index_places_on_lat_and_lng | 2 | lng | A | 1373712 | NULL | NULL | YES | BTREE | |
も関連すると思われるん
これらは(:場所、[:、LAT:LNG]レール移行ADD_INDEXで作られる)インデックスに統計でありますはるかに簡単なクエリでバウンディングボックスの結果のために類似したクエリを実行すると、計算をtrigのが、それは同様にひどく実行します。
Place.find(:all,:bounds=>GeoKit::Bounds.from_point_and_radius(latlng,10)).length
は、類似した実行計画を提供します:
mysql> explain SELECT * FROM `places` WHERE ((places.lat>51.3373601471464 AND places.lat<51.6264998528536 AND places.lng>-1.13302245886176 AND places.lng<-0.668737541138245)) ;
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | places | range | index_places_on_lat_and_lng | index_places_on_lat_and_lng | 10 | NULL | 87554 | 100.00 | Using where |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
これは興味深いことです。その場合、どのようなインデックスが必要ですか? – frankodwyer
ありがとうございます - それはより良く動作するように聞こえ、私はそれを試してみます。空間を使わずに現在のクエリを改善する方法もあります(geokitは現在mysql空間的なものを使用していません)?このクエリーを実行すると面白いことに – frankodwyer
WHERE((places.lat> 51.3373601471464 AND places.lat <51.6264998528536));それは42078行だけを返します!だから、mysqlはその部分の素晴らしい仕事をしていないようです。 – frankodwyer