2009-08-26 8 views
4

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 | 
    +----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+ 

答えて

3

プレーンB-Treeインデックスはこのようなクエリにはあまり適していません。クエリの

は、 rangeのアクセス方法は、以下の条件で使用されます。
places.lat > 51.3373601471464 AND places.lat < 51.6264998528536 

、これも考慮に lonを取ることはありません。

あなたは、空間的な能力を使用したい場合、あなたは、Pointsとしてあなたの場所を維持し、それらのSPATIALインデックスを作成し、バウンディングボックスをフィルタリングするMBRContainsを使用する必要があります。

ALTER TABLE places ADD place_point GEOMETRY 

CREATE SPATIAL INDEX sx_places_points ON places (place_point) 

UPDATE places 
SET  place_point = Point(lat, lon) 

SELECT * 
FROM places 
WHERE MBRContains(LineString(Point(51.3373, -1.1330), Point(51.6264, -0.6687)), place_point) 
     AND -- do the fine filtering here 

を更新:

CREATE TABLE t_spatial (id INT NOT NULL, lat FLOAT NOT NULL, lon FLOAT NOT NULL, coord GEOMETRY) ENGINE=MyISAM; 

INSERT 
INTO t_spatial (id, lat, lon) 
VALUES (1, 52.2532, 20.9778); 

UPDATE t_spatial 
SET  coord = Point(lat, lon); 

これは私のために5.1.35で動作します。

+0

これは興味深いことです。その場合、どのようなインデックスが必要ですか? – frankodwyer

+0

ありがとうございます - それはより良く動作するように聞こえ、私はそれを試してみます。空間を使わずに現在のクエリを改善する方法もあります(geokitは現在mysql空間的なものを使用していません)?このクエリーを実行すると面白いことに – frankodwyer

+0

WHERE((places.lat> 51.3373601471464 AND places.lat <51.6264998528536));それは42078行だけを返します!だから、mysqlはその部分の素晴らしい仕事をしていないようです。 – frankodwyer

関連する問題