2009-08-26 51 views
4

我在rails應用程序中使用geokit(acts_as_mappable),並且在有大量模型時(我嘗試使用1-2百萬個但是問題無疑在早期開始使用)時,徑向或邊界搜索的性能顯着降低比這個)。mysql是否使用我的索引,並且可以改進geokit的性能?

Geokit根據表格(緯度和經度)中的lat和lng列進行所有計算。爲了提高性能,geokit通常會添加一個bounding box'where'子句,目的是在經度和緯度上使用組合索引來提高性能。但是,對於大量模型來說,它仍然非常慢,而且在我看來,邊界框子句應該比它更有幫助。

所以我的問題是,有沒有辦法讓mysql更好地使用組合的經緯度/指數或以其他方式改善geokit sql查詢的性能?或者,lat/lng的組合索引可以更有幫助嗎?

編輯:我有這個工作與軌現在更詳細的書面解決了here

更多背景

例如,下面的查詢查找10英里給定的範圍內的所有地方點。 (我已經添加了.length來確定返回的結果數量 - 在geokit中有更好的方式來說明這一點,但我想強制更典型的SQL查詢)。

Place.find(:all,:origin=>latlng,:within=>10).length 

Mac mini需要14秒左右。這裏是解釋計劃

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

因此MySQL正在檢查87554行,即使地方的結果數爲1135(與實際邊界框位置的數量僅僅是1323)。

以上是關於索引的統計信息(其與導軌遷移add_index製成:地方,[:LAT,:LNG]):

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

也不它似乎與到三角函數的計算,因爲這樣做類似的查詢的邊框導致更簡單的查詢,但它執行同樣糟糕:

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 2009-08-26 11:23:12

+0

謝謝 - 這聽起來像它會工作得更好,我會嘗試一下。還有一種方法可以在不使用空間的情況下改進當前查詢(因爲geokit目前不使用mysql空間)。 – frankodwyer 2009-08-26 11:32:04

+0

有趣的是,如果我運行這個查詢SELECT * FROM'places' WHERE((places.lat> 51.3373601471464 AND places.lat <51.6264998528536));它只返回42078行!所以它看起來像MySQL也沒有很好的工作。 – frankodwyer 2009-08-26 11:35:56

相關問題