通過GeoHash核心原理來分析hbase rowkey設計

 

 

   注:本文是結合hbase實戰以及網上的博文概述了一下,以作後期使用時的備份。
   參考資料:http://www.cnblogs.com/LBSer/p/3310455.html


   百度地圖,美團,大衆點評等等等等,都會有查找附近的功能,如何實現呢?計算所在位置P與北京所有餐館的距離,然後返回距離<=1000米的餐館。餐館何其多啊,這樣計算不得了,既然知道經緯度了,那它應該知道自己在西城區,那應該計算所在位置P與西城區所有餐館的距離啊,機機運用了遞歸的思想,想到了西城區也很多餐館啊,應該計算所在位置P與所在街道所有餐館的距離,這樣計算量又小了,效率也提升了。通過過濾的方法來減小參與計算的餐館數目,從某種角度上講,就是索引技術。

     首先我們先來看一下上一篇文章中使用到的複合rowkey在空間索引中爲什麼不適合。首先每個點都有一個X點和Y點,我們先按經度在按維度來組合一個行健然後將各個點連接起來是什麼效果:如下


     我們可以看到1到9之間的排序,因爲先按經度在按維度,所以會出現這種南北位置跳躍存儲的情況。所以說空間位置不一定就是hbase的存儲位置,在存儲空間字段時候我們要同時考慮經度和維度,因爲它們同等重要
      GeoHash將二維的經緯度轉換成字符串,比如下圖展示了北京9個區域的GeoHash字符串,分別是WX4ER,WX4G2、WX4G3等等,每一個字符串代表了某一矩形區域。也就是說,這個矩形區域內所有的點(經緯度座標)都共享相同的GeoHash字符串,這樣既可以保護隱私(只表示大概區域位置而不是具體的點),又比較容易做緩存,比如左上角這個區域內的用戶不斷髮送位置信息請求餐館數據,由於這些用戶的GeoHash字符串都是WX4ER,所以可以把WX4ER當作key,把該區域的餐館信息當作value來進行緩存,而如果不使用GeoHash的話,由於區域內的用戶傳來的經緯度是各不相同的,很難做緩存。
     

字符串越長,表示的範圍越精確。如圖所示,5位的編碼能表示10平方千米範圍的矩形區域,而6位編碼能表示更精細的區域(約0.34平方千米)


字符串相似的表示距離相近(特殊情況後文闡述),這樣可以利用字符串的前綴匹配來查詢附近的POI信息。如下兩個圖所示,一個在城區,一個在郊區,城區的GeoHash字符串之間比較相似,郊區的字符串之間也比較相似,而城區和郊區的GeoHash字符串相似程度要低些。


通過上面的介紹我們知道了GeoHash就是一種將經緯度轉換成字符串的方法,並且使得在大部分情況下,字符串前綴匹配越多的距離越近,回到我們的案例,根據所在位置查詢來查詢附近餐館時,只需要將所在位置經緯度轉換成GeoHash字符串,並與各個餐館的GeoHash字符串進行前綴匹配,匹配越多的距離越近。

下面以北海公園爲例介紹GeoHash算法的計算步驟
根據經緯度計算GeoHash二進制編碼

地球緯度區間是[-90,90], 北海公園的緯度是39.928167,可以通過下面算法對緯度39.928167進行逼近編碼:

1)區間[-90,90]進行二分爲[-90,0),[0,90],稱爲左右區間,可以確定39.928167屬於右區間[0,90],給標記爲1;

2)接着將區間[0,90]進行二分爲 [0,45),[45,90],可以確定39.928167屬於左區間 [0,45),給標記爲0;

3)遞歸上述過程39.928167總是屬於某個區間[a,b]。隨着每次迭代區間[a,b]總在縮小,並越來越逼近39.928167;

4)如果給定的緯度x(39.928167)屬於左區間,則記錄0,如果屬於右區間則記錄1,這樣隨着算法的進行會產生一個序列1011100,序列的長度跟給定的區間劃分次數有關。

根據緯度算編碼

bit min mid max
1 -90.000 0.000 90.000
0 0.000 45.000 90.000
1 0.000 22.500 45.000
1 22.500 33.750 45.000
1 33.7500 39.375 45.000
0 39.375 42.188 45.000
0 39.375 40.7815 42.188
0 39.375 40.07825 40.7815
1 39.375 39.726625 40.07825
1 39.726625 39.9024375 40.07825

    

同理,地球經度區間是[-180,180],可以對經度116.389550進行編碼。

根據經度算編碼

bit min mid max
1 -180 0.000 180
1 0.000 90 180
0 90 135 180
1 90 112.5 135
0 112.5 123.75 135
0 112.5 118.125 123.75
1 112.5 115.3125 118.125
0 115.3125 116.71875 118.125
1 115.3125 116.015625 116.71875
1 116.015625 116.3671875 116.71875



通過上述計算,緯度產生的編碼爲10111 00011,經度產生的編碼爲11010 01011。偶數位放經度,奇數位放緯度,把2串編碼組合生成新串:11100 11101 00100 01111。

最後使用用0-9、b-z(去掉a, i, l, o)這32個字母進行base32編碼,首先將11100 11101 00100 01111轉成十進制,對應着28、29、4、15,十進制對應的編碼就是wx4g。同理,將編碼轉換成經緯度的解碼算法與之相反,具體不再贅述。

可以看出,當geohash base32編碼長度爲8時,精度在19米左右,而當編碼長度爲9時,精度在2米左右,編碼長度需要根據數據情況進行選擇。


上文講了GeoHash的計算步驟,僅僅說明是什麼而沒有說明爲什麼?爲什麼分別給經度和維度編碼?爲什麼需要將經緯度兩串編碼交叉組合成一串編碼?本節試圖回答這一問題。

如圖所示,我們將二進制編碼的結果填寫到空間中,當將空間劃分爲四塊時候,編碼的順序分別是左下角00,左上角01,右下腳10,右上角11,也就是類似於Z的曲線,當我們遞歸的將各個塊分解成更小的子塊時,編碼的順序是自相似的(分形),每一個子快也形成Z曲線,這種類型的曲線被稱爲Peano空間填充曲線。

這種類型的空間填充曲線的優點是將二維空間轉換成一維曲線(事實上是分形維),對大部分而言,編碼相似的距離也相近, 但Peano空間填充曲線最大的缺點就是突變性,有些編碼相鄰但距離卻相差很遠,比如0111與1000,編碼是相鄰的,但距離相差很大。



由於GeoHash是將區域劃分爲一個個規則矩形,並對每個矩形進行編碼,這樣在查詢附近POI信息時會導致以下問題,比如紅色的點是我們的位置,綠色的兩個點分別是附近的兩個餐館,但是在查詢的時候會發現距離較遠餐館的GeoHash編碼與我們一樣(因爲在同一個GeoHash區域塊上),而較近餐館的GeoHash編碼與我們不一致。這個問題往往產生在邊界處。



解決的思路很簡單,我們查詢時,除了使用定位點的GeoHash編碼進行匹配外,還使用周圍8個區域的GeoHash編碼,這樣可以避免這個問題。

2)我們已經知道現有的GeoHash算法使用的是Peano空間填充曲線,這種曲線會產生突變,造成了編碼雖然相似但距離可能相差很大的問題,因此在查詢附近餐館時候,首先篩選GeoHash編碼相似的POI點,然後進行實際距離計算。

  • 大小: 93.1 KB
  • 大小: 116.3 KB
  • 大小: 92.2 KB
  • 大小: 219.8 KB
  • 大小: 37 KB
  • 大小: 27.8 KB
  • 大小: 92.3 KB
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章