【学习笔记】Redis的geohash数据结构介绍
geohash介紹
?Redis 3.2開始,Redis基于geohash和有序集合提供了地理位置相關功能。Redis Geo模塊包含了以下6個命令:
?GEOADD: 將給定的位置對象(緯度、經度、名字)添加到指定的key;
?GEOPOS: 從key??返回所有給定位置對象的位置(經度和緯度);
?GEODIST: 返回兩個給定位置之間的距離;
?GEOHASH: 返回?個或多個位置對象的Geohash表?;
?GEORADIUS: 以給定的經緯度為中?,返回?標集合中與中?的距離不超過給定最?距離的所有位置對象;
?GEORADIUSBYMEMBER: 以給定的位置對象為中?,返回與其距離不超過給定最?距離的所有位置對象。
其中,組合使?GEOADD和GEORADIUS可實現“附近的?”中“增”和“查”的基本功能。要實現微信中“附近的?”功能,可直接使?GEORADIUSBYMEMBER命令。其中“給定的位置對象”即為??本?,搜索的對象為其他??。不過本質
上,GEORADIUSBYMEMBER = GEOPOS + GEORADIUS,即先查找??位置再通過該位置搜索附近滿?位置相互距離條件的其他??對象。
以下會從源碼?度??對GEOADD和GEORADIUS命令進?分析,剖析其算法原理
Redis geo操作中只包含了“增”和“查”的操作,并沒有專?的“刪除”命令。主要是因為Redis內部使?有序集合(zset)保存位置對象,可?zrem進?刪除。
在Redis源碼geo.c的?件注釋中,只說明了該?件為GEOADD、GEORADIUSGEORADIUSBYMEMBER的實現?件(其實在也實現了另三個命令)。從側?看出其他三個命令為輔助命令。
使??式
GEOADD key longitude latitude member [longitude latitude member …]
將給定的位置對象(緯度、經度、名字)添加到指定的key。
其中,key為集合名稱,member為該經緯度所對應的對象。在實際運?中,當所需存儲的對象數量過多時,可通過設置多
key(如?個省?個key)的?式對對象集合變相做sharding,避免單集合數量過多。
成功插?后的返回值:
(integer) N
其中N為成功插?的個數。
通過源碼分析可以看出Redis內部使?有序集合(zset)保存位置對象,有序集合中每個元素都是?個帶位置的對象,元素的score值為其經緯度對應的52位的geohash值。
double類型精度為52位;
geohash是以base32的?式編碼,52bits最?可存儲10位geohash值,對應地理區域??為0.60.6?的格?。換句話說經Redis geo轉換過的位置理論上會有約0.31.414=0.424?的誤差。(?)
簡單總結下GEOADD命令都?了啥:
1、參數提取和校驗;
2、將?參經緯度轉換為52位的geohash值(score);
3、調?ZADD命令將member及其對應的score存?集合key中。
使??式
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] [STORE key] [STORedisT key]以給定的經緯度為中?,返回?標集合中與中?的距離不超過給定最?距離的所有位置對象。
范圍單位:m | km | ft | mi --> ? | 千? | 英尺 | 英?
額外外參數:
- WITHDIST:在返回位置對象的同時,將位置對象與中?之間的距離也?并返回。距離的單位和??給定的范圍單位保持?致。
- WITHCOORD:將位置對象的經度和維度也?并返回。
- WITHHASH:以 52 位有符號整數的形式,返回位置對象經過原始 geohash 編碼的有序集合分值。這個選項主要?于底層應?
或者調試,實際中的作?并不?。 - ASC|DESC:從近到遠返回位置對象元素 | 從遠到近返回位置對象元素。
- COUNT count:選取前N個匹配位置對象元素。(不設置則返回所有元素) - STORE key:將返回結果的地理位置信息保存到
指定key。 - STORedisT key:將返回結果離中?點的距離保存到指定key。
由于 STORE 和 STORedisT 兩個選項的存在,GEORADIUS 和 GEORADIUSBYMEMBER 命令在技術上會被標記為寫?命令,從?只會查詢(寫?)主實例,QPS過?時容易造成主實例讀寫壓?過?。為解決這個問題,在 Redis 3.2.10 和 Redis 4.0.0 中,分別新增了 GEORADIUS_RO 和 GEORADIUSBYMEMBER_RO兩個只讀命令。
不過,在實際開發中筆者發現 在java package Redis.clients.jedis.params.geo 的 GeoRadiusParam 參數類中并不包含STORE 和 STORedisT 兩個參數選項,在調?georadius時是否真的只查詢了主實例,還是進?了只讀封裝。
成功查詢后的返回值:
不帶WITH限定,返回?個member list,如:
[“member1”,“member2”,“member3”]
帶WITH限定,member list中每個member也是?個嵌套list,如:
[
[“member1”, distance1, [longitude1, latitude1]]
[“member2”, distance2, [longitude2, latitude2]]
]
?結
拋開眾多可選參數不談,簡單總結下GEORADIUS命令是怎么利?geohash獲取?標位置對象的:
1、參數提取和校驗;
2、利?中?點和輸?半徑計算待查區域范圍。這個范圍參數包括滿?條件的最?的geohash?格等級(精度) 以及 對應的能夠覆蓋?標區域的九宮格位置;(后續會有詳細說明)
3、對九宮格進?遍歷,根據每個geohash?格的范圍框選出位置對象。進?步找出與中?點距離?于輸?半徑的對象,進?返回。
直接描述不太好理解,我們通過如下兩張圖在對算法進?簡單的演?:
令左圖的中?為搜索中?,綠?圓形區域為?標區域,所有點為待搜索的位置對象,紅?點則為滿?條件的位置對象。
在實際搜索時,?先會根據搜索半徑計算geohash?格等級(即右圖中?格??等級),并確定九宮格位置(即紅?九宮格位置信息);再依次查找計算九宮格中的點(藍點和紅點)與中?點的距離,最終篩選出距離范圍內的點(紅點)。
如何通過geohash?格的范圍框選出元素對象?效率如何?
?先在每個geohash?格中的geohash值都是連續的,有固定范圍。所以只要找出有序集合中,處在該范圍的位置對象即可。以下是有序集合的跳表數據結構:
其擁有類似?叉查找樹的查詢效率,操作平均時間復雜性為O(log(N))。
且最底層的所有元素都以鏈表的形式按序排列。
所以在查詢時,只要找到集合中處在?標geohash?格中的第?個值,后續依次對?即可,不?多次查找。九宮格不能?起查,要?個個遍歷的原因也在于九宮格各?格對應的geohash值不具有連續性。只有連續了,查詢效率才會?,不然要多做許多距離運算。
總結
以上是生活随笔為你收集整理的【学习笔记】Redis的geohash数据结构介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用jedis访问Redis进行对象存取示
- 下一篇: mysql 共享锁和排他锁 意向锁 记录