Redis 應用-Geo

Gundy發表於2019-07-05

系列文章

移動網際網路時代LBS應用越來越多,交友軟體中附近的小姐姐、外賣軟體中附近的美食店鋪、叫車軟體附近的車輛等等,那這種附近各種形形色色的XX是如何實現的呢,

我麼你都知道地球上的地理位置是使用二維的經緯度表示,經度範圍 (-180, 180],緯度範圍 (-90, 90],只要我們確定一個點的經緯度就可以名曲他在地球的位置

例如叫車,最直觀的操作就是實時記錄更新各個車的位置,然後去我們要找車時,在資料庫中查詢距離我們(座標x0,y0)附近r公里的車輛,使用如下SQL即可:

select cart from position where x0-r < x < x0+r and y0-r < y < y0+r

但是這樣會有什麼問題呢?
1是查詢效能問題,如果併發高,資料量大這種查詢是要搞垮資料庫的
2是這個查詢的是一個矩形訪問,而不是以我為中心r公里為半徑的圓形訪問。
3是精準度的問題,我們知道地球不是平面座標系,而是一個圓球,這種矩形計算在長距離計算時會有很大誤差

今天我們講的是使用Redis來解決附近的問題,下面我們先看看Redis的解決辦法

Redis 地理位置

命令

Redis在3.2版本以後增加了地理位置的處理,其提供了6個地理位置相關的命令

  • GEOADD 將給定的空間元素(緯度、經度、名字)新增到指定的鍵裡面
  • GEOPOS 從鍵裡面返回所有給定位置元素的位置(經度和緯度)
  • GEODIST 返回兩個給定位置之間的距離。
  • GEORADIUS 以給定的經緯度為中心, 返回與中心的距離不超過給定最大距離的所有位置元素。
  • GEORADIUSBYMEMBER 跟GEORADIUS類似
  • GEOHASH 返回一個或多個位置元素的 Geohash 表示。

使用

新增地理位置
geo add key longitude latitude member [longitude latitude member ...]
如新增杭州北京上海的地理位置
127.0.0.1:6379> geoadd city 120.20000 30.26667 hangzhou  116.41667 39.91667 beijing 121.47 31.23 shanghai
獲取地理位置資訊

geopos 指令可以獲取集合中任意元素的經緯度座標,可以一次獲取多個。

127.0.0.1:6379> geopos city hangzhou  beijing shanghai
1) 1) "120.15000075101852417"
   2) "30.2800007575645509"
2) 1) "116.39999896287918091"
   2) "39.90000009167092543"
3) 1) "121.47000163793563843"
   2) "31.22999903975783553"
127.0.0.1:6379> geopos city hangzhou
1) 1) "120.15000075101852417"
   2) "30.2800007575645509"
計算距離

距離單位可以是 m、km、ml、ft,分別代表米、千米、英里和尺。

127.0.0.1:6379> geodist city shanghai hangzhou  km
"164.5694"
127.0.0.1:6379> geodist city beijing  hangzhou  km
"1122.7998"
獲取指定元素範圍的地理資訊位置集合

使用GEORADIUSBYMEMBER命令即可查詢附近的位置

GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

例如查詢距離杭州300km以內的城市的10個城市按距離排序

127.0.0.1:6379> GEORADIUSBYMEMBER city hangzhou 300 km WITHCOORD WITHDIST WITHHASH  ASC COUNT 10
1) 1) "hangzhou"
   2) "0.0000"
   3) (integer) 4054134257390783
   4) 1) "120.15000075101852417"
      2) "30.2800007575645509"
2) 1) "shanghai"
   2) "164.5694"
   3) (integer) 4054803462927619
   4) 1) "121.47000163793563843"
      2) "31.22999903975783553"

在給定以下可選項時, 命令會返回額外的資訊:

  • WITHDIST : 在返回位置元素的同時, 將位置元素與中心之間的距離也一併返回。 距離的單位和使用者給定的範圍單位保持一致。
  • WITHCOORD : 將位置元素的經度和維度也一併返回。
  • WITHHASH : 以 52 位有符號整數的形式, 返回位置元素經過原始 geohash 編碼的有序集合分值。
  • ASC : 根據中心的位置, 按照從近到遠的方式返回位置元素。DESC : 根據中心的位置, 按照從遠到近的方式返回位置元素。
獲取元素的 hash 值

可能你還注意到有一個命令GEOHASH,那他是做什麼的呢

127.0.0.1:6379> geohash city hangzhou
1) "wtmkq069cc0"
127.0.0.1:6379> geohash city beijing
1) "wx4fbxxfke0"

返回的其實是元素的經緯度經過goehash計算後的base32編碼字串。可以通過連線 http://geohash.org/${hash}中進行直接定位,它是 geohash 的標準編碼值。

RedisGeo的原理

RedisGeo的儲存結構

其儲存結構主要使用的是Redis的有序結構,其score是GeoHash的52位整數值

127.0.0.1:6379> ZRANGE city 0 -1 WITHSCORES
1) "hangzhou"
2) "4054134257390783"
3) "shanghai"
4) "4054803462927619"
5) "beijing"
6) "4069885360207904"

Geohash原理

其原理比較容易理解,核心思想就是將球體轉換為球面,區塊轉換為一點

image

主要分為三步

  • 將三維的地球變為二維的座標
  • 在將二維的座標轉換為一維的點塊
  • 最後將一維的點塊轉換為二進位制在通過base32編碼

詳細原理解析可以參考這邊文章GeoHash核心原理解析

其他Geo處理

目前很多資料儲存引擎都支援Geo的處理,如MongoDB、MySql、PgSql、Elasticsearch等。感興趣的讀者可以去研究一下。

參考文章

Redis 命令參考
GeoHash核心原理解析

本文亦在微信公眾號【小道資訊】釋出,歡迎掃碼關注!
image

相關文章