文章版權由做者李曉暉和博客園共有,若轉載請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/。html
將採集的POI入庫後,數據庫裏保存有該POI的位置描述、X、Y等信息。當須要進行逆編碼查詢時,前端傳入座標的X、Y值,後臺構建查詢範圍查詢,而且對查詢出來的值進行距離排序。前端
a.前端查詢url中的X、Y值爲真實值,可能會暴露相關真實信息。算法
b.前端查詢的url由於X、Y值的長度而變得比較長。數據庫
c.後臺中,須要同時對X列、Y列作查詢判斷。緩存
d.由於傳入的X、Y值總在變化,數據庫中的查詢很難進行緩存優化。微信
e.數據庫中保存的是真實X、Y數據,增長了存儲空間。網絡
Geohash的初衷是如何用盡可能短的URL來標誌地圖上的某個位置,而地圖上的位置通常是用經緯度來表示,問題就轉化爲如何把經緯度轉化爲一個儘可能短的URL。性能
具體來講GeoHash算法的主要思想是對某一數字經過二分法進行無限逼近。這裏以經緯度區間(經度(-180,180),緯度(-90,90))爲例子來進行講解。測試
假設緯度值爲48,精確到1度便可,則其編碼流程以下所示:優化
以左向爲0,右向爲1,最後48精確到1度的編碼爲:11000010。
對經度一樣能夠作該編碼逼近。
對經緯度分別作了編碼後,須要將兩個編碼進行融合。融合規則爲:將經度和緯度的編碼合併,奇數位是緯度,偶數位是經度。
好比:緯度39.92324精確到0.001後的編碼爲1011 1000 1100 0111 1001。經度116.3906精確到0.001後的編碼爲1101 0010 1100 0100 0100。二者融合後的編碼爲:11100 11101 00100 01111 00000 01101 01011 00001。
這裏使用Base32算法對編碼進行字符串 化,Base32的規則以下:
咱們將39.92324, 116.3906(11100 11101 00100 01111 00000 01101 01011 00001)進行Base32的字符串化後得到字符串:wx4g0ec1。
互聯網GeoHash算法主要針對的是經緯度系統,因此其算法中的編碼範圍、編碼精確度都相對固定。
範圍爲[-90,90],[-180,180]。
在緯度相等的狀況下:
經度每隔0.00001度,距離相差約1米;
每隔0.0001度,距離相差約10米;
每隔0.001度,距離相差約100米;
每隔0.01度,距離相差約1000米;
每隔0.1度,距離相差約10000米。
在經度相等的狀況下:
緯度每隔0.00001度,距離相差約1.1米;
每隔0.0001度,距離相差約11米;
每隔0.001度,距離相差約111米;
每隔0.01度,距離相差約1113米;
每隔0.1度,距離相差約11132米。
Geohash,若是geohash的位數是6位數的時候,大概爲附近1公里。
可是假如須要編碼的座標爲平面座標時,以上算法必須進行相關修改才能使用。
這裏以平面座標系爲例子。平面座標的單位是M,若是想要編碼的精確度精確到1M,則須要算出此時須要保留的編碼長度應該是多少。具體算法與哈夫曼編碼的思路基本相同,如下是代碼截取:
這裏,須要知道編碼範圍、編碼精確位數。由於5位數的編碼等於一個Base32字符串,因此最後返回的值除以5。
固然,這裏是經緯度座標系也能夠,只要規定好範圍以及編碼精確位數便可。
編碼時,編碼範圍再也不是固定的經緯度範圍,而是根據傳遞進來的範圍值來進行編碼。具體代碼以下:
這裏我準備了13萬條數據(測試數據中大量重複數據):
數據爲平面座標系數據,對全部數據已經進行了GeoHash編碼,爲了方便測試,這裏一樣存入了X、Y座標。
測試座標點爲:505214.06,305104.09。對其進行精確到100M的GeoHash編碼,編碼值爲:kx5。
查詢中爲了去掉重複項,因此稍顯繁瑣。
查詢範圍爲(CoordinateX < 505314.06 and CoordinateX > 505114.06 and CoordinateY < 305204.09 and CoordinateY >305004.09)具體以下,共查詢到:12項。
由於編碼已經精確到100M了,因此直接等於該編碼便可,查詢獲得的結果爲6項。
對比兩種查詢,很明顯GeoHash編碼查詢法查詢到的數據要少一些。仔細分析可見:在傳統查詢中,距離在50M之內的前6項,均出如今了GeoHash查詢中。可是其餘6項則沒有。
咱們能夠推斷爲,雖然編碼精度設置的爲100M,可是最後一位的編碼應該具體是精確到了100/2=50M 的範圍。
因此,咱們能夠推斷:GeoHash是能大體查詢出要求範圍的數據的,精確度比較高,可是查詢所得數據量比真實的範圍查詢要少。
清除查詢緩存後,進行範圍查詢,須要0.281S。
在X、Y上分別創建索引後,須要0.172S。
不創建索引,須要耗時0.499S。
創建索引後,耗時0.156S。
第二次命中時,耗時:0.031S。
不考慮網絡環境、查詢時電腦自己CPU等性能偶然影響,單純測試結果以下:
查詢類型 (數據量13W) |
無索引查詢時間 |
有索引查詢時間 |
二次命中時間 |
普通地理編碼查詢 |
0.281S |
跟索引創建方式有關係,單純在XY上創建索引,時間反而更久,須要:0.172S |
0.094S |
GeoHash編碼查詢 |
0.499S |
0.156S |
0.031S |
可見GeoHash由於是字符串查詢,其自己是比較耗時的。可是當作了索引後,其查詢速度是快於普通查詢的,並且其二次命中時查詢速度比普通查詢二次命中會更快。其緣由比較簡單:單列索引、單列命中顯然是高於多列的。
普通查詢的資源消耗信息:
GeoHash查詢的資源消耗信息:
其中:
cardinality是指計劃中這一步所處理的行數。
cost指cbo中這一步所耗費的資源,這個值是相對值,和cpu_cost、io_cost是有關係的。
cost是由其餘幾個因素共同決定的,這裏暫時不進行深刻的研究。通常狀況下,在一張表只有一條記錄的狀況下,cpu_cost會有個初始值(常見的是2萬多或3萬多),隨着記錄的增長,cpu_cost也成比例的增長。對於io_cost來講,若是訪問的記錄在一個db_block中,值是不變的。
bytes指cbo中這一步所處理全部記錄的字節數,是估算出來的一組值。
對比性能分析表可見:
GeoHash表中的cardinality和bytes是明顯低於普通查詢的,究其緣由也仍是由於其只需查詢一列便可。
GeoHash算法的幾個特色:
a.GeoHash編碼後,得到的位置信息爲範圍信息,而非真實的座標精確值。
b.GeoHash編碼後,將X、Y座標融合成一個值,數據庫存在中既能夠減小存儲空間,也便於優化查詢。尤爲是編碼後,必定範圍內的點均是一樣編碼,興趣點查詢的二次命中率會大大提升,進一步加快查詢速度。
c.GeoHash的編碼能夠容易的表示出範圍包含關係,這樣很是便於進行範圍查詢。
d.查詢時前端URL長度變短。
GeoHash查詢出來的僅僅是某個範圍內的數據,須要對返回的數據在進行距離運算,排序後最近的即是。其優化效率主要體如今範圍查詢上。
測試在1W條數據如下時不明顯。
10W條附近時,開始有0.1S間的小差距。
類推,當數據量越大時,效果越明顯。
這一點是有些用戶對geohash的誤解,雖然geo確實儘量的將位置相近的點hash到了一塊兒,但是這並非嚴格意義上的(實際上也並不可能,由於畢竟多一維座標),例如在方格4的左下部分的點和大方格1的右下部分的點離的很近,但是它們的geohash值必定是相差的至關遠,由於頭一次的分塊就相差太大了,不少時候我 們對geohash的值進行簡單的排序比較,結果貌似真的可以找出相近的點,而且彷佛仍是按照距離的遠近排列的,但是實際上會有一些點被漏掉了。
上述這個問題,能夠經過搜索一個格子,周圍八個格子的數據,統一獲取後再進行過濾。這樣就在編碼層次解決了這個問題。
既然不能作到將相近的點hash值也相近,那麼geohash的意義何在呢?
我的覺以爲geohash仍是至關有用的一個算法,畢竟這個算法經過無窮的細分,能確保將每個小塊的geohash值確保在必定的範圍以內,這樣就爲靈活的周邊查找和範圍查找提供了可能。
這個問題是前幾天一個讀博士的朋友問的我,思考這個問題挺有趣的。其中會涉及到長距離和短距離問題,推薦一篇相似博客:咱們看到的地圖一直都錯得離譜(http://blog.sina.com.cn/s/blog_517eed9f0102w4rm.html);
-----歡迎轉載,但保留版權,請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/
若是您以爲本文確實幫助了您,能夠微信掃一掃,進行小額的打賞和鼓勵,謝謝 ^_^