redis GEO地理位置


說起redis的GEO特性,最大的貢獻還是咱們中國人。redis作者在對3.2引進新特性的博客中介紹了為什么支持GEO。GEO hashing的api是在Ardb實現的,Ardb是github用戶yinqiwen實現的基於redis協議實現的nosql系統,Ardb支持除了redis、還有LevelDB、RocksDB
、LMDB等kv引擎。其中Ardb實現了GEO hashing功能。從Ardb作者的用戶名和標識的位置在深圳可以看出Ardb作者應該是咱中國人。Ardb是用c++寫的。redis另一個開發者Matt Stancliff從Ardb提取GEO庫,用C語言改寫,整合進redis的一個自己的分支,並被redis作者接受,合並進了3.2版本。GEO目前提供以下6個命令。
1、geoadd:增加某個地理位置的坐標。
2、geopos:獲取某個地理位置的坐標。
3、geodist:獲取兩個地理位置的距離。
4、georadius:根據給定地理位置坐標獲取指定范圍內的地理位置集合。
5、georadiusbymember:根據給定地理位置獲取指定范圍內的地理位置集合。
6、geohash:獲取某個地理位置的geohash值。
地理位置的坐標是以WGS84為標准,WGS84,全稱World Geodetic System 1984,是為GPS全球定位系統使用而建立的坐標系統。
GEO命令
下面來看看具體每個命令的用法。
geoadd
geoadd用來增加地理位置的坐標,可以批量添加地理位置,命令格式為:
GEOADD key longitude latitude member [longitude latitude member ...]
key標識一個地理位置的集合。longitude latitude member標識了一個地理位置的坐標。longitude是地理位置的經度,latitude是地理位置的緯度。member是該地理位置的名稱。GEOADD可以批量給集合添加一批地理位置。
geopos
geopos可以獲取地理位置的坐標,可以批量獲取多個地理位置的坐標,命令格式為:
GEOPOS key member [member ...]
geodist
geodist用來獲取兩個地理位置的距離,命令格式為:
GEODIST key member1 member2 [m|km|ft|mi]
單位可以指定為以下四種類型:
m:米,距離單位默認為米,不傳遞該參數則單位為米。
km:公里。
mi:英里。
ft:英尺。
georadius
georadius可以根據給定地理位置坐標獲取指定范圍內的地理位置集合。命令格式為:
GEORADIUS key longitude latitude radius [m|km|ft|mi] [WITHCOORD] [WITHDIST] [ASC|DESC] [WITHHASH] [COUNT count]
longitude latitude標識了地理位置的坐標,radius表示范圍距離,距離單位可以為m|km|ft|mi,還有一些可選參數:
WITHCOORD:傳入WITHCOORD參數,則返回結果會帶上匹配位置的經緯度。
WITHDIST:傳入WITHDIST參數,則返回結果會帶上匹配位置與給定地理位置的距離。
ASC|DESC:默認結果是未排序的,傳入ASC為從近到遠排序,傳入DESC為從遠到近排序。
WITHHASH:傳入WITHHASH參數,則返回結果會帶上匹配位置的hash值。
COUNT count:傳入COUNT參數,可以返回指定數量的結果。
georadiusbymember
georadiusbymember可以根據給定地理位置獲取指定范圍內的地理位置集合。georadius命令傳遞的是坐標,georadiusbymember傳遞的是地理位置。georadius更為靈活,可以獲取任何坐標點范圍內的地理位置。但是大多數時候,只是想獲取某個地理位置附近的其他地理位置,使用georadiusbymember則更為方便。georadiusbymember命令格式為(命令可選參數與georadius含義一樣):
GEORADIUSBYMEMBER key member radius [m|km|ft|mi] [WITHCOORD] [WITHDIST] [ASC|DESC] [WITHHASH] [COUNT count]
geohash
geohash可以獲取某個地理位置的geohash值。geohash是將二維的經緯度轉換成字符串hash值的算法,后面會具體介紹geohash原理。可以批量獲取多個地理位置的geohash值。命令格式為:
GEOHASH key member [member ...]
redis GEO實現
redis GEO實現主要包含了以下兩項技術:
1、使用geohash保存地理位置的坐標。
2、使用有序集合(zset)保存地理位置的集合。
geohash
geohash的思想是將二維的經緯度轉換成一維的字符串,geohash有以下三個特點:
1、字符串越長,表示的范圍越精確。編碼長度為8時,精度在19米左右,而當編碼長度為9時,精度在2米左右。
2、字符串相似的表示距離相近,利用字符串的前綴匹配,可以查詢附近的地理位置。這樣就實現了快速查詢某個坐標附近的地理位置。
3、geohash計算的字符串,可以反向解碼出原來的經緯度。
這三個特性讓geohash特別適合表示二維hash值。這篇文章:GeoHash核心原理解析詳細的介紹了geohash的原理,想要了解geohash實現的朋友可以參考這篇文章。
redis GEO命令實現
知道了redis使用有序集合(zset)保存地理位置數據(想了解redis有序集合的,可以參看這篇文章《有序集合對象》),以及geohash的特性,就很容易理解redis是如何實現redis GEO命令了。細心的讀者可能發現,redis沒有實現地理位置的刪除命令。不過由於GEO數據保存在zset中,可以用zrem來刪除某個地理位置。
geoadd命令增加地理位置的時候,會先計算地理位置坐標的geohash值,然后地理位置作為有序集合的member,geohash作為該member的score。然后使用zadd命令插入到有序集合。
geopos命令則先根據地理位置獲取geohash值,然后decode得到地理位置的坐標。
geodist命令先根據兩個地理位置各自得到坐標,然后計算兩個坐標的距離。
georadius和georadiusbymember使用相同的實現,georadiusbymember多了一步把地理位置轉換成對應的坐標。然后查找該坐標和周圍對應8個坐標符合距離要求的地理位置。因為geohash得到的值其實是個格子,並不是點,這樣通過計算周圍對應8個坐標就能解決邊緣問題。由於使用有序集合保存地理位置,在對地列位置基於范圍查詢,就相當於實現了zrange命令,內部的實現確實與zrange命令一致,只是geo有些特別的處理,比如獲得的某個地理位置,還需要計算該地理位置是否符合給定的距離訪問。
geohash則直接返回了地理位置的geohash值。
redis關於geohash使用了Ardb的geohash庫geohash-int,redis使用的geohash編碼長度為26位。可以精確到0.59m的精度。
總結
通過本文,撥開GEO身后的雲霧,可以看出redis借助了有序集合(zset)和geohash,加上redis本身實現的命令框架,可以很容易的實現地理位置相關的命令

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM