mysql經緯度查詢並且計算2KM范圍內附近用戶的sql查詢性能優化實例教程
之前很傻很天真地以為無非就是逐個計算距離,然后比較出來就行了,然后當碰到訪問用戶很多,而且數據庫中經緯度信息很多的時候,計算量的迅速增長,能讓服務器完全傻逼掉,還是老前輩的經驗比我們豐富,給了我很大的啟示。
MySQL性能調優 – 使用更為快速的算法進行距離計算
最近遇到了一個問題,通過不斷的嘗試最終將某句原本占據近1秒的查詢優化到了0.01秒,效率提高了100倍.
問題是這樣的,有一張存放用戶居住地點經緯度信息的MySQL數據表,表結構可以簡化 為:id(int),longitude(long),latitude()long. 而業務系統中有一個功能是查找離某個用戶最近的其余數個用戶,通過代碼分析,可以確定原先的做法基本是這樣的:
//需要查詢的用戶的坐標
$lat=20; $lon=20;//執行查詢,算出該用戶與所有其他用戶的距離,取出最近的10個 $sql='select * from users_location order by ACOS(SIN(('.$lat.' * 3.1415) / 180 ) *SIN((latitude * 3.1415) / 180 ) +COS(('.$lat.' * 3.1415) / 180 ) * COS((latitude * 3.1415) / 180 ) *COS(('.$lon.' * 3.1415) / 180 - (longitude * 3.1415) / 180 ) ) * 6380 asc limit 10';
而這條sql執行的速度卻非常緩慢,用了近1秒的時間才返回結果,應該是因為order里的子語句用了太多的數學計算公式,導致整體的運算速度下降.
而在實際的使用中,不太可能會發生需要計算該用戶與所有其他用戶的距離,然后再排序的情況,當用戶數量達到一個級別時,就可以在一個較小的范圍里進行搜索,而非在所有用戶中進行搜索.
所以對於這個例子,我增加了4個where條件,只對於經度和緯度大於或小於該用戶1度(111公里)范圍內的用戶進行距離計算,同時對數據表中的經度和緯度兩個列增加了索引來優化where語句執行時的速度.
最終的sql語句如下
$sql='select * from users_location where latitude > '.$lat.'-1 and latitude < '.$lat.'+1 and longitude > '.$lon.'-1 and longitude < '.$lon.'+1 order by ACOS(SIN(('.$lat.' * 3.1415) / 180 ) *SIN((latitude * 3.1415) / 180 ) +COS(('.$lat.' * 3.1415) / 180 ) * COS((latitude * 3.1415) / 180 ) *COS(('.$lon.'* 3.1415) / 180 - (longitude * 3.1415) / 180 ) ) * 6380 asc limit 10';
經過優化的sql大大提高了運行速度,在某些情況下甚至有100倍的提升.這種從業務角度出發,縮小sql查詢范圍的方法也可以適用在其他地方.