Redis實戰篇(四)基於GEO實現查找附近的人功能

如果現在要開發一個功能:

要為一款交友App實現查找附近的人,並按距離進行排序。

image

讓你來開發這個功能,你會如何實現?

MySQL 不合適

你可能想到,把用戶用戶的經緯度坐標使用MySQL等關係資料庫(用戶id,經度x,緯度y)存儲,但是該如何計算距離和排序呢?

不可能通過遍歷來計算所有的用戶和目標用戶的距離,然後再進行排序,因為這個計算量太大了,性能指標肯定無法滿足。

GeoHash的編碼方法

為了能高效地對經緯度進行比較,Redis 採用了業界廣泛使用的 GeoHash 編碼方法,這個方法的基本原理是「二分區間,區間編碼」。

關於 GeoHash 參考 //www.cnblogs.com/LBSer/p/3310455.html

簡單來說,GeoHash 能夠將二維的經緯度轉換為字元串,然後位置就能夠直接進行比較和範圍查詢了。

Redis 中 Geo 的使用

命令 說明  可用版本 時間複雜度
GEOADD 添加位置的經緯度 >= 3.2.0 O(logN)
GEOPOS 返回位置的經緯度 >= 3.2.0 O(logN)
GEODIST 返回兩個位置的距離 >= 3.2.0 O(logN)
GEORADIUS 返回與指定位置距離距離不大於指定值的位置的經緯度 >= 3.2.0 O(N+logM)
GEORADIUSBYMEMBER 這個命令和 GEORADIUS 命令一樣 >= 3.2.0 O(logN+M)
GEOHASH 返回位置的 GeoHash 值 >= 3.2.0 O(logN)

 

 示例

假設用戶ID是33,經緯度位置是(116.054579, 39.030452),我們可以用一個 GEO 集合保存所有用戶的經緯度,集合 key 是 users:locations。執行下面的這個命令,就可以把ID號為33的用戶的當前經緯度位置存入GEO集合中:

GEOADD users:locations 116.034579 39.030452 33

 

當用戶想要尋找自己附近的人時,就可以使用 GEORADIUS 命令。

例如,執行下面的命令,Redis 會根據輸入的用戶的經緯度資訊(116.054579, 39.030452),查找以這個經緯度為中心的5公里內的用戶資訊。

GEORADIUS users:locations 116.054579 39.030452 5 km ASC COUNT 10

 

總結

在一個地圖應用中,車的數據、餐館的數據、人的數據可能會有百萬千萬條,如果使用 Redis 的 Geo 數據結構,它們將全部放在一個 Sorted Set 集合中。在 Redis 的集群環境中,集合可能會從一個節點遷移到另一個節點,如果單個 key 的數據過大,會對集群的遷移工作造成較大的影響,在集群環境中單個 key 對應的數據量不宜超過 1M,否則會導致集群遷移出現卡頓現象,影響線上服務的正常運行。

所以,這裡建議 Geo 的數據使用單獨的 Redis 實例部署,不使用集群環境。

如果數據量過億甚至更大,就需要對 Geo 數據進行拆分,按國家拆分、按省拆分,按市拆分,在人口特大城市甚至可以按區拆分。這樣就可以顯著降低單個 Sorted Set 集合的大小。

參考資料