Redis 的5種基礎數據結構和3種高級數據結構

Redis有5個基本數據結構,string、list、hash、set和zset。它們是日常開發中使用頻率非常高應用最爲廣泛的數據結構,把這5個數據結構都吃透了,你就掌握了Redis應用知識的一半了。

string

首先我們從string談起。string表示的是一個可變的字節數組,我們初始化字符串的內容、可以拿到字符串的長度,可以獲取string的字串,可以覆蓋string的字串內容,可以追加子串。

Redis的字符串是動態字符串,是可以修改的字符串,內部結構實現上類似於Java的ArrayList,採用預分配冗餘空間的方式來減少內存的頻繁分配,如圖中所示,內部爲當前字符串實際分配的空間capacity一般要高於實際字符串長度len。當字符串長度小於1M時,擴容都是加倍現有的空間,如果超過1M,擴容時一次只會多擴1M的空間。需要注意的是字符串最大長度爲512M。

初始化字符串 需要提供「變量名稱」和「變量的內容」

> set ireader beijing.zhangyue.keji.gufen.youxian.gongsi
OK

獲取字符串的內容 提供「變量名稱」

> get ireader
"beijing.zhangyue.keji.gufen.youxian.gongsi"

獲取字符串的長度 提供「變量名稱」

> strlen ireader
(integer) 42

獲取子串 提供「變量名稱」以及開始和結束位置[start, end]

> getrange ireader 28 34
"youxian"

覆蓋子串 提供「變量名稱」以及開始位置和目標子串

> setrange ireader 28 wooxian
(integer) 42  # 返回長度
> get ireader
"beijing.zhangyue.keji.gufen.wooxian.gongsi"

追加子串

> append ireader .hao
(integer) 46 # 返回長度
> get ireader
"beijing.zhangyue.keji.gufen.wooxian.gongsi.hao"

遺憾的是字符串沒有提供字串插入方法和子串刪除方法。

計數器
如果字符串的內容是一個整數,那麼還可以將字符串當成計數器來使用。

> set ireader 42
OK
> get ireader
"42"
> incrby ireader 100
(integer) 142
> get ireader
"142"
> decrby ireader 100
(integer) 42
> get ireader
"42"
> incr ireader  # 等價於incrby ireader 1
(integer) 143
> decr ireader  # 等價於decrby ireader 1
(integer) 142

計數器是有範圍的,它不能超過Long.Max,不能低於Long.MIN

> set ireader 9223372036854775807
OK
> incr ireader
(error) ERR increment or decrement would overflow
> set ireader -9223372036854775808
OK
> decr ireader
(error) ERR increment or decrement would overflow

過期和刪除 字符串可以使用del指令進行主動刪除,可以使用expire指令設置過期時間,到點會自動刪除,這屬於被動刪除。可以使用ttl指令獲取字符串的壽命。

> expire ireader 60
(integer) 1  # 1表示設置成功,0表示變量ireader不存在
> ttl ireader
(integer) 50  # 還有50秒的壽命,返回-2表示變量不存在,-1表示沒有設置過期時間
> del ireader
(integer) 1  # 刪除成功返回1
> get ireader
(nil)  # 變量ireader沒有了

list

Redis將列表數據結構命名爲list而不是array,是因爲列表的存儲結構用的是鏈表而不是數組,而且鏈表還是雙向鏈表。因爲它是鏈表,所以隨機定位性能較弱,首尾插入刪除性能較優。如果list的列表長度很長,使用時我們一定要關注鏈表相關操作的時間複雜度。

負下標 鏈表元素的位置使用自然數0,1,2,....n-1表示,還可以使用負數-1,-2,...-n來表示,-1表示「倒數第一」,-2表示「倒數第二」,那麼-n就表示第一個元素,對應的下標爲0

隊列/堆棧 鏈表可以從表頭和表尾追加和移除元素,結合使用rpush/rpop/lpush/lpop四條指令,可以將鏈表作爲隊列或堆棧使用,左向右向進行都可以

# 右進左出
> rpush ireader go
(integer) 1
> rpush ireader java python
(integer) 3
> lpop ireader
"go"
> lpop ireader
"java"
> lpop ireader
"python"
# 左進右出
> lpush ireader go java python
(integer) 3
> rpop ireader
"go"
...
# 右進右出
> rpush ireader go java python
(integer) 3
> rpop ireader 
"python"
...
# 左進左出
> lpush ireader go java python
(integer) 3
> lpop ireader
"python"
...

在日常應用中,列表常用來作爲異步隊列來使用。

長度 使用llen指令獲取鏈表長度

> rpush ireader go java python
(integer) 3
> llen ireader
(integer) 3

隨機讀 可以使用lindex指令訪問指定位置的元素,使用lrange指令來獲取鏈表子元素列表,提供start和end下標參數

> rpush ireader go java python
(integer) 3
> lindex ireader 1
"java"
> lrange ireader 0 2
1) "go"
2) "java"
3) "python"
> lrange ireader 0 -1  # -1表示倒數第一
1) "go"
2) "java"
3) "python"

使用lrange獲取全部元素時,需要提供end_index,如果沒有負下標,就需要首先通過llen指令獲取長度,纔可以得出end_index的值,有了負下標,使用-1代替end_index就可以達到相同的效果。

修改元素 使用lset指令在指定位置修改元素。

> rpush ireader go java python
(integer) 3
> lset ireader 1 javascript
OK
> lrange ireader 0 -1
1) "go"
2) "javascript"
3) "python"

插入元素 使用linsert指令在列表的中間位置插入元素,有經驗的程序員都知道在插入元素時,我們經常搞不清楚是在指定位置的前面插入還是後面插入,所以antirez在linsert指令裏增加了方向參數before/after來顯示指示前置和後置插入。不過讓人意想不到的是linsert指令並不是通過指定位置來插入,而是通過指定具體的值。這是因爲在分佈式環境下,列表的元素總是頻繁變動的,意味着上一時刻計算的元素下標在下一時刻可能就不是你所期望的下標了。

> rpush ireader go java python
(integer) 3
> linsert ireader before java ruby
(integer) 4
> lrange ireader 0 -1
1) "go"
2) "ruby"
3) "java"
4) "python"

到目前位置,我還沒有在實際應用中發現插入指定的應用場景。

刪除元素 列表的刪除操作也不是通過指定下標來確定元素的,你需要指定刪除的最大個數以及元素的值

> rpush ireader go java python
(integer) 3
> lrem ireader 1 java
(integer) 1
> lrange ireader 0 -1
1) "go"
2) "java"

定長列表 在實際應用場景中,我們有時候會遇到「定長列表」的需求。比如要以走馬燈的形式實時顯示中獎用戶名列表,因爲中獎用戶實在太多,能顯示的數量一般不超過100條,那麼這裏就會使用到定長列表。維持定長列表的指令是ltrim,需要提供兩個參數start和end,表示需要保留列表的下標範圍,範圍之外的所有元素都將被移除。

> rpush ireader go java python javascript ruby erlang rust cpp
(integer) 8
> ltrim ireader -3 -1
OK
> lrange ireader 0 -1
1) "erlang"
2) "rust"
3) "cpp"

如果指定參數的end對應的真實下標小於start,其效果等價於del指令,因爲這樣的參數表示需要需要保留列表元素的下標範圍爲空。

快速列表

如果再深入一點,你會發現Redis底層存儲的還不是一個簡單的linkedlist,而是稱之爲快速鏈表quicklist的一個結構。首先在列表元素較少的情況下會使用一塊連續的內存存儲,這個結構是ziplist,也即是壓縮列表。它將所有的元素緊挨着一起存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改成quicklist。因爲普通的鏈表需要的附加指針空間太大,會比較浪費空間。比如這個列表裏存的只是int類型的數據,結構上還需要兩個額外的指針prev和next。所以Redis將鏈表和ziplist結合起來組成了quicklist。也就是將多個ziplist使用雙向指針串起來使用。這樣既滿足了快速的插入刪除性能,又不會出現太大的空間冗餘。

hash

哈希等價於Java語言的HashMap或者是Python語言的dict,在實現結構上它使用二維結構,第一維是數組,第二維是鏈表,hash的內容key和value存放在鏈表中,數組裏存放的是鏈表的頭指針。通過key查找元素時,先計算key的hashcode,然後用hashcode對數組的長度進行取模定位到鏈表的表頭,再對鏈表進行遍歷獲取到相應的value值,鏈表的作用就是用來將產生了「hash碰撞」的元素串起來。Java語言開發者會感到非常熟悉,因爲這樣的結構和HashMap是沒有區別的。哈希的第一維數組的長度也是2^n。

增加元素 可以使用hset一次增加一個鍵值對,也可以使用hmset一次增加多個鍵值對

> hset ireader go fast
(integer) 1
> hmset ireader java fast python slow
OK

獲取元素 可以通過hget定位具體key對應的value,可以通過hmget獲取多個key對應的value,可以使用hgetall獲取所有的鍵值對,可以使用hkeys和hvals分別獲取所有的key列表和value列表。這些操作和Java語言的Map接口是類似的。

> hmset ireader go fast java fast python slow
OK
> hget ireader go
"fast"
> hmget ireader go python
1) "fast"
2) "slow"
> hgetall ireader
1) "go"
2) "fast"
3) "java"
4) "fast"
5) "python"
6) "slow"
> hkeys ireader
1) "go"
2) "java"
3) "python"
> hvals ireader
1) "fast"
2) "fast"
3) "slow"

刪除元素 可以使用hdel刪除指定key,hdel支持同時刪除多個key

> hmset ireader go fast java fast python slow
OK
> hdel ireader go
(integer) 1
> hdel ireader java python
(integer) 2

判斷元素是否存在 通常我們使用hget獲得key對應的value是否爲空就直到對應的元素是否存在了,不過如果value的字符串長度特別大,通過這種方式來判斷元素存在與否就略顯浪費,這時可以使用hexists指令。

> hmset ireader go fast java fast python slow
OK
> hexists ireader go
(integer) 1

計數器 hash結構還可以當成計數器來使用,對於內部的每一個key都可以作爲獨立的計數器。如果value值不是整數,調用hincrby指令會出錯。

> hincrby ireader go 1
(integer) 1
> hincrby ireader python 4
(integer) 4
> hincrby ireader java 4
(integer) 4
> hgetall ireader
1) "go"
2) "1"
3) "python"
4) "4"
5) "java"
6) "4"
> hset ireader rust good
(integer) 1
127.0.0.1:6379> hincrby ireader rust 1
(error) ERR hash value is not an integer

擴容 當hash內部的元素比較擁擠時(hash碰撞比較頻繁),就需要進行擴容。擴容需要申請新的兩倍大小的數組,然後將所有的鍵值對重新分配到新的數組下標對應的鏈表中(rehash)。如果hash結構很大,比如有上百萬個鍵值對,那麼一次完整rehash的過程就會耗時很長。這對於單線程的Redis裏來說有點壓力山大。所以Redis採用了漸進式rehash的方案。它會同時保留兩個新舊hash結構,在後續的定時任務以及hash結構的讀寫指令中將舊結構的元素逐漸遷移到新的結構中。這樣就可以避免因擴容導致的線程卡頓現象。

縮容 Redis的hash結構不但有擴容還有縮容,從這一點出發,它要比Java的HashMap要厲害一些,Java的HashMap只有擴容。縮容的原理和擴容是一致的,只不過新的數組大小要比舊數組小一倍。

set

Java程序員都知道HashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個對象。Redis的set結構也是一樣,它的內部也使用hash結構,所有的value都指向同一個內部值。

增加元素 可以一次增加多個元素

> sadd ireader go java python
(integer) 3

讀取元素 使用smembers列出所有元素,使用scard獲取集合長度,使用srandmember獲取隨機count個元素,如果不提供count參數,默認爲1

> sadd ireader go java python
(integer) 3
> smembers ireader
1) "java"
2) "python"
3) "go"
> scard ireader
(integer) 3
> srandmember ireader
"java"

刪除元素 使用srem刪除一到多個元素,使用spop刪除隨機一個元素

> sadd ireader go java python rust erlang
(integer) 5
> srem ireader go java
(integer) 2
> spop ireader
"erlang"

判斷元素是否存在 使用sismember指令,只能接收單個元素

> sadd ireader go java python rust erlang
(integer) 5
> sismember ireader rust
(integer) 1
> sismember ireader javascript
(integer) 0

sortedset

SortedSet(zset)是Redis提供的一個非常特別的數據結構,一方面它等價於Java的數據結構Map<String, Double>,可以給每一個元素value賦予一個權重score,另一方面它又類似於TreeSet,內部的元素會按照權重score進行排序,可以得到每個元素的名次,還可以通過score的範圍來獲取元素的列表。

zset底層實現使用了兩個數據結構,第一個是hash,第二個是跳躍列表,hash的作用就是關聯元素value和權重score,保障元素value的唯一性,可以通過元素value找到相應的score值。跳躍列表的目的在於給元素value排序,根據score的範圍獲取元素列表。

增加元素 通過zadd指令可以增加一到多個value/score對,score放在前面

> zadd ireader 4.0 python
(integer) 1
> zadd ireader 4.0 java 1.0 go
(integer) 2

長度 通過指令zcard可以得到zset的元素個數

> zcard ireader
(integer) 3

刪除元素 通過指令zrem可以刪除zset中的元素,可以一次刪除多個

> zrem ireader go python
(integer) 2

計數器 同hash結構一樣,zset也可以作爲計數器使用。

> zadd ireader 4.0 python 4.0 java 1.0 go
(integer) 3
> zincrby ireader 1.0 python
"5"

獲取排名和分數 通過zscore指令獲取指定元素的權重,通過zrank指令獲取指定元素的正向排名,通過zrevrank指令獲取指定元素的反向排名[倒數第一名]。正向是由小到大,負向是由大到小。

> zscore ireader python
"5"
> zrank ireader go  # 分數低的排名考前,rank值小
(integer) 0
> zrank ireader java
(integer) 1
> zrank ireader python
(integer) 2
> zrevrank ireader python
(integer) 0

根據排名範圍獲取元素列表 通過zrange指令指定排名範圍參數獲取對應的元素列表,攜帶withscores參數可以一併獲取元素的權重。通過zrevrange指令按負向排名獲取元素列表[倒數]。正向是由小到大,負向是由大到小。

> zrange ireader 0 -1  # 獲取所有元素
1) "go"
2) "java"
3) "python"
127.0.0.1:6379> zrange ireader 0 -1 withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrange ireader 0 -1 withscores
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"

根據score範圍獲取列表 通過zrangebyscore指令指定score範圍獲取對應的元素列表。通過zrevrangebyscore指令獲取倒排元素列表。正向是由小到大,負向是由大到小。參數-inf表示負無窮,+inf表示正無窮。

> zrangebyscore ireader 0 5
1) "go"
2) "java"
3) "python"
> zrangebyscore ireader -inf +inf withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrangebyscore ireader +inf -inf withscores  # 注意正負反過來了
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"

根據範圍移除元素列表 可以通過排名範圍,也可以通過score範圍來一次性移除多個元素

> zremrangebyrank ireader 0 1
(integer) 2  # 刪掉了2個元素
> zadd ireader 4.0 java 1.0 go
(integer) 2
> zremrangebyscore ireader -inf 4
(integer) 2
> zrange ireader 0 -1
1) "python"

跳躍列表 zset內部的排序功能是通過「跳躍列表」數據結構來實現的,它的結構非常特殊,也比較複雜。這一塊的內容深度讀者要有心理準備。

因爲zset要支持隨機的插入和刪除,所以它不好使用數組來表示。我們先看一個普通的鏈表結構。

我們需要這個鏈表按照score值進行排序。這意味着當有新元素需要插入時,需要定位到特定位置的插入點,這樣纔可以繼續保證鏈表是有序的。通常我們會通過二分查找來找到插入點,但是二分查找的對象必須是數組,只有數組纔可以支持快速位置定位,鏈表做不到,那該怎麼辦?

想想一個創業公司,剛開始只有幾個人,團隊成員之間人人平等,都是聯合創始人。隨着公司的成長,人數漸漸變多,團隊溝通成本隨之增加。這時候就會引入組長制,對團隊進行劃分。每個團隊會有一個組長。開會的時候分團隊進行,多個組長之間還會有自己的會議安排。公司規模進一步擴展,需要再增加一個層級——部門,每個部門會從組長列表中推選出一個代表來作爲部長。部長們之間還會有自己的高層會議安排。

跳躍列表就是類似於這種層級制,最下面一層所有的元素都會串起來。然後每隔幾個元素挑選出一個代表來,再將這幾個代表使用另外一級指針串起來。然後在這些代表裏再挑出二級代表,再串起來。最終就形成了金字塔結構。

想想你老家在世界地圖中的位置:亞洲-->中國->安徽省->安慶市->樅陽縣->湯溝鎮->田間村->xxxx號,也是這樣一個類似的結構。

「跳躍列表」之所以「跳躍」,是因爲內部的元素可能「身兼數職」,比如上圖中間的這個元素,同時處於L0、L1和L2層,可以快速在不同層次之間進行「跳躍」。

定位插入點時,先在頂層進行定位,然後下潛到下一級定位,一直下潛到最底層找到合適的位置,將新元素插進去。你也許會問那新插入的元素如何纔有機會「身兼數職」呢?

跳躍列表採取一個隨機策略來決定新元素可以兼職到第幾層,首先L0層肯定是100%了,L1層只有50%的概率,L2層只有25%的概率,L3層只有12.5%的概率,一直隨機到最頂層L31層。絕大多數元素都過不了幾層,只有極少數元素可以深入到頂層。列表中的元素越多,能夠深入的層次就越深,能進入到頂層的概率就會越大。

這還挺公平的,能不能進入中央不是靠拼爹,而是看運氣。

 

再來看3個高級數據結構:Bitmaps,Hyperloglogs,GEO

Bitmaps

bitmaps不是一個真實的數據結構。而是String類型上的一組面向bit操作的集合。由於strings是二進制安全的blob,並且它們的最大長度是512m,所以bitmaps能最大設置2^32個不同的bit。

bit操作被分爲兩組:

  • 恆定時間的單個bit操作,例如把某個bit設置爲0或者1。或者獲取某bit的值。

  • 對一組bit的操作。例如給定範圍內bit統計(例如人口統計)。

Bitmaps的最大優點就是存儲信息時可以節省大量的空間。例如在一個系統中,不同的用戶被一個增長的用戶ID表示。40億(2^32=4*1024*1024*1024≈40億)用戶只需要512M內存就能記住某種信息,例如用戶是否登錄過。

Bits設置和獲取通過SETBIT 和GETBIT 命令,用法如下:

SETBIT key offset value
GETBIT key offset

使用實例:

127.0.0.1:6380> setbit dupcheck 10 1
(integer) 0
127.0.0.1:6380> getbit dupcheck 10 
(integer) 1

SETBIT命令第一個參數是位編號,第二個參數是這個位的值,只能是0或者1。如果bit地址超過當前string長度,會自動增大string。

Bitmaps示意圖

GETBIT命令指示返回指定位置bit的值。超過範圍(尋址地址在目標key的string長度以外的位)的GETBIT總是返回0。三個操作bits組的命令如下:

  • BITOP 執行兩個不同string的位操作.,包括AND,OR,XOR和NOT.

  • BITCOUNT 統計位的值爲1的數量。

  • BITPOS 尋址第一個爲0或者1的bit的位置(尋址第一個爲1的bit的位置:bitpos dupcheck 1; 尋址第一個爲0的bit的位置:bitpos dupcheck 0).

bitmaps一般的使用場景:

  • 各種實時分析.

  • 存儲與對象ID關聯的節省空間並且高性能的布爾信息.

例如,想象一下你想知道訪問你的網站的用戶的最長連續時間。你開始計算從0開始的天數,就是你的網站公開的那天,每次用戶訪問網站時通過SETBIT命令設置bit爲1,可以簡單的用當前時間減去初始時間併除以3600*24(結果就是你的網站公開的第幾天)當做這個bit的位置。

這種方法對於每個用戶,都有存儲每天的訪問信息的一個很小的string字符串。通過BITCOUN就能輕易統計某個用戶歷史訪問網站的天數。另外通過調用BITPOS命令,或者客戶端獲取並分析這個bitmap,就能計算出最長停留時間。

HyperLogLogs

HyperLogLog是用於計算唯一事物的概率數據結構(從技術上講,這被稱爲估計集合的基數)。如果統計唯一項,項目越多,需要的內存就越多。因爲需要記住過去已經看過的項,從而避免多次統計這些項。

然而,有一組算法可以交換內存以獲得精確度:在redis的實現中,您使用標準錯誤小於1%的估計度量結束。這個算法的神奇在於不再需要與需要統計的項相對應的內存,取而代之,使用的內存一直恆定不變。最壞的情況下只需要12k,就可以計算接近2^64個不同元素的基數。或者如果您的HyperLogLog(我們從現在開始簡稱它爲HLL)已經看到的元素非常少,則需要的內存要要少得多。

在redis中HLL是一個不同的數據結構,它被編碼成Redis字符串。因此可以通過調用GET命令序列化一個HLL,也可以通過調用SET命令將其反序列化到redis服務器。

HLL的API類似使用SETS數據結構做相同的任務,SETS結構中,通過SADD命令把每一個觀察的元素添加到一個SET集合,用SCARD命令檢查SET集合中元素的數量,集合裏的元素都是唯一的,已經存在的元素不會被重複添加。

而使用HLL時並不是真正添加項到HLL中(這一點和SETS結構差異很大),因爲HLL的數據結構只包含一個不包含實際元素的狀態,API是一樣的:

  • PFADD命令用於添加一個新元素到統計中。

  • PFCOUNT命令用於獲取到目前爲止通過PFADD命令添加的唯一元素個數近似值。

  • PFMERGE命令執行多個HLL之間的聯合操作。

127.0.0.1:6380> PFADD hll a b c d d c
(integer) 1
127.0.0.1:6380> PFCOUNT hll
(integer) 4
127.0.0.1:6380> PFADD hll e
(integer) 1
127.0.0.1:6380> PFCOUNT hll
(integer) 5

PFMERGE命令說明:bashbash

PFMERGE destkey sourcekey [sourcekey ...]
Merge N different HyperLogLogs into a single one.

用法(把hll1和hll2合併到hlls中):bashbash

127.0.0.1:6380> PFADD hll1 1 2 3
(integer) 1
127.0.0.1:6380> PFADD hll2 3 4 5
(integer) 1
127.0.0.1:6380> PFMERGE hlls hll1 hll2
OK
127.0.0.1:6380> PFCOUNT hlls

HLL數據結構的一個使用場景就是計算用戶每天在搜索框中執行的唯一查詢,即搜索頁面UV統計。而Bitmaps則用於判斷某個用戶是否訪問過搜索頁面。這是它們用法的不同。

GEO

Redis的GEO特性在 Redis3.2版本中推出,這個功能可以將用戶給定的地理位置(經度和緯度)信息儲存起來,並對這些信息進行操作。GEO相關命令只有6個:

  • GEOADD:GEOADD key longitude latitude member [longitude latitude member …],將指定的地理空間位置(緯度、經度、名稱)添加到指定的key中。例如:GEOADD city 113.501389 22.405556 shenzhen

經緯度具體的限制,由EPSG:900913/EPSG:3785/OSGEO:41001規定如下:
有效的經度從-180度到180度。
有效的緯度從-85.05112878度到85.05112878度。
當座標位置超出上述指定範圍時,該命令將會返回一個錯誤。
  • GEOHASH:GEOHASH key member [member …],返回一個或多個位置元素的標準Geohash值,它可以在http://geohash.org/使用。查詢例子:http://geohash.org/sqdtr74hyu0.(可以通過谷歌瞭解Geohash原理,或者戳Geohash基本原理:https://www.cnblogs.com/tgzhu/p/6204173.html)。

  • GEOPOS:GEOPOS key member [member …],從key裏返回所有給定位置元素的位置(經度和緯度)。

  • GEODIST:GEODIST key member1 member2 [unit],返回兩個給定位置之間的距離。GEODIST命令在計算距離時會假設地球爲完美的球形。在極限情況下,這一假設最大會造成0.5%的誤差。

指定單位的參數unit必須是以下單位的其中一個:
m 表示單位爲米(默認)。
km 表示單位爲千米。
mi 表示單位爲英里。
ft 表示單位爲英尺。
  • GEORADIUS:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],以給定的經緯度爲中心, 返回鍵包含的位置元素當中, 與中心的距離不超過給定最大距離的所有位置元素。這個命令可以查詢某城市的周邊城市羣。

  • GEORADIUSBYMEMBER:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],這個命令和GEORADIUS命令一樣,都可以找出位於指定範圍內的元素,但是GEORADIUSBYMEMBER的中心點是由給定的位置元素決定的,而不是像 GEORADIUS那樣,使用輸入的經度和緯度來決定中心點。

指定成員的位置被用作查詢的中心。

GEO的6個命令用法示例如下:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2

redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"

redis> GEOPOS Sicily Palermo Catania NonExisting
1) 1) "13.361389338970184"
   2) "38.115556395496299"
2) 1) "15.087267458438873"
   2) "37.50266842333162"
3) (nil)

redis> GEODIST Sicily Palermo Catania
"166274.15156960039"

redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"

redis> GEORADIUSBYMEMBER Sicily Agrigento 100 km
1) "Agrigento"
2) "Palermo"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章