Redis(03)-GeoHash原理及使用

GeoHash

適用場景

當(dāng)需要查詢“附近的人員”這種功能的時候,如果使用mysql數(shù)據(jù)庫這種方式存儲經(jīng)緯度的信息,指定一定的范圍信息后,再通過計算當(dāng)前人員距離值進(jìn)行排序,這個過程,只要是數(shù)據(jù)查詢量稍微大一點就會出現(xiàn)性能問題,好在redis提供現(xiàn)成的解決方案GeoHash

實現(xiàn)原理

Redis中實現(xiàn)的GeoHash算法是將顯示中的地點信息轉(zhuǎn)化成一個長度為52的整數(shù)。然后將其存放在zset里面(其底層是使用zset進(jìn)行實現(xiàn)的。 我們可以使用zrem 進(jìn)行數(shù)據(jù)的刪除。),zset的value值就是用戶的ID ,score值就是GeoHash的52位整數(shù)值,在redis中使用的時候,通過zset的score排序就可以得到附近的元素,然后在將score值還原成經(jīng)緯度坐標(biāo)信息即可

使用方式

  • geoadd:加入經(jīng)緯度以及集合名稱的多個三維數(shù)組
    • (例:geoadd company 116.48105 33.999484 jili)加入吉利汽車公司的地址
    • (例:geoadd company 146.48105 77.999484 baoma)加入寶馬公司的地址
  • geodist:用于計算兩個元素之間的距離,攜帶集合名稱,兩個節(jié)點的名稱和距離單位
    • (例:geodist company baoma jili km)獲取寶馬和吉利公司的距離(單位km)
  • geopos:獲取集合元素中任意元素的經(jīng)緯度坐標(biāo)
    • (例:geodist company baoma)獲取寶馬公司的經(jīng)緯度坐標(biāo)(會稍微有一些誤差)
  • georadiusbymember:最主要的方法,用來指定查詢元素附近的其他元素
    • (例:georadiusbymember company baoma 20 km count 3 desc)獲取寶馬公司附近20km內(nèi)最多三個元素按照距離倒序

注意:在真是場景中使用Geo的時候如果業(yè)務(wù)數(shù)據(jù)過大,例如順分車查找附件的人,人員和車比較多的時候,所有的數(shù)據(jù)都存儲在一個zset中,就會導(dǎo)致一個節(jié)點過大的問題,所以可以對數(shù)據(jù)根據(jù)地理位置進(jìn)行拆分,例如順風(fēng)車可以根據(jù)不同的城市,或者不同的區(qū),使用不同的zset

原理

GeoHash算法使用二分切割法,將二維的經(jīng)緯度數(shù)據(jù)映射到一維的整數(shù),這樣所有的元素都將掛載到一條線上,地球緯度區(qū)間是[-90,90],經(jīng)度區(qū)間是[-180,180]。 將它展開想象長一個矩形



通過剛才的方法,我們能夠?qū)⒌厍虻谋砻孓D(zhuǎn)換成二維空間的平面。那接下來要將二維轉(zhuǎn)變成一維。如果切割二維空間,可以切割出很多正方形。如何表示這個正方形呢?最簡單的方法是在平面上進(jìn)行遍歷。每遍歷到一個點,就給它標(biāo)注一個值,比如00、01、10、11,隨著二進(jìn)制數(shù)字增加,相當(dāng)于遍歷面上不同的位置。



當(dāng)將空間劃分為四塊時候,編碼的順序分別是左下角00,左上角01,右下腳10,右上角11,也就是類似于Z的曲線。當(dāng)我們遞歸的將各個塊分解成更小的子塊時可以標(biāo)識更小的空間范圍(如上圖二中所示),如從0000開始到1111結(jié)束編碼的順序是自相似的(分形),每一個子快也形成Z曲線,這種類型的曲線被稱為Peano空間填充曲線

Geohash 通過將節(jié)點數(shù)據(jù)編碼轉(zhuǎn)換成一維數(shù)據(jù),(base 32 和 base 36) 會將落到網(wǎng)格中的二進(jìn)制數(shù)據(jù)編碼成字符串,再使用B樹索引快速查找出需要的數(shù)據(jù)。

Java使用案例(偽代碼)

import redis.clients.jedis.*;
import redis.clients.jedis.params.geo.GeoRadiusParam;

/**
  * 增加地理位置的坐標(biāo)
  * @param key
  * @param coordinate
  * @param memberId
  * @return
  */
    public static Long geoadd(String key, GeoCoordinate coordinate, String memberId) {
        Jedis jedis = jedisPool.getResource();
        jedis.geoadd(key,coordinate.getLongitude(),coordinate.getLatitude(),memberName);
        jedis.close();
    }

/**
  * 增加地理位置的坐標(biāo)
  * @param key
  * @param coordinate
  * @param memberId
  * @return
  */
    public static Long geoadd(String key, GeoCoordinate coordinate, String memberId) {
        Jedis jedis = jedisPool.getResource();
        jedis.geoadd(key,coordinate.getLongitude(),coordinate.getLatitude(),memberName);
        jedis.close();
    }

/**
  * 根據(jù)給定地理位置坐標(biāo)獲取指定范圍內(nèi)的地理位置集合
  *(返回匹配位置的經(jīng)緯度 + 匹配位置與給定地理位置的距離 + 從近到遠(yuǎn)排序,)
  * @param key
  * @param coordinate
  * @param radius
  * @return  List<GeoRadiusResponse>
  */
    public static List<GeoRadiusResponse> geoRadius(String key, GeoCoordinate coordinate, double radius) {
            Jedis jedis= jedisPool.getResource();
            List<GeoRadiusResponse> list =  jedis.georadius(
                        key, 
                        coordinate.getLongitude(), 
                        coordinate.getLatitude(), 
                        radius,
                        GeoUnit.KM, 
                        GeoRadiusParam.geoRadiusParam().withDist().withCoord().sortAscending());
            jedis.close();
            return  
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容