建設(shè)門戶網(wǎng)站所需廣告門
Redis常用數(shù)據(jù)結(jié)構(gòu)與應(yīng)用場(chǎng)景
redis中存儲(chǔ)數(shù)據(jù)是以key-value
鍵值對(duì)的方式去存儲(chǔ)的,其中key為string字符類型,value的數(shù)據(jù)類型可以是string(字符串)、list(列表)、hash(字典)、set(集合) 、 zset(有序集合)。 這5種數(shù)據(jù)類型在開(kāi)發(fā)中可以應(yīng)對(duì)大部分場(chǎng)景的存儲(chǔ)
拓展:key的底層存儲(chǔ)方式SDS
這里有一個(gè)問(wèn)題,當(dāng)我們使用一條redis命令set key value
的時(shí)候,redis進(jìn)行了什么操作?
其實(shí)當(dāng)我們使用命令操作redis的時(shí)候,也是會(huì)經(jīng)過(guò)redis客戶端到redis服務(wù)端的過(guò)程,這些命令相當(dāng)于一個(gè)請(qǐng)求
- redis客戶端通過(guò)socket傳輸這些命令
- redis服務(wù)端通過(guò)io讀取到這些請(qǐng)求命令,把所有的命令解析成一個(gè)字符串,并執(zhí)行對(duì)應(yīng)命令操作,然后再經(jīng)過(guò)socket寫回操作結(jié)果!
同時(shí)redis是使用c語(yǔ)言寫的,但它底層在存放redis的key時(shí),并沒(méi)有用c語(yǔ)言原生的字符串?dāng)?shù)據(jù)結(jié)構(gòu),而是定義了一個(gè)屬于redis的數(shù)據(jù)結(jié)構(gòu)SDS(Simple Dynamic String),
struct sdshdr{//記錄buf數(shù)組中已使用字節(jié)的數(shù)量//等于 SDS 保存字符串的長(zhǎng)度int len;//記錄 buf 數(shù)組中未使用字節(jié)的數(shù)量int free;//字節(jié)數(shù)組,用于保存字符串char buf[];
}
為什么要用這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu)去存儲(chǔ)字符串呢?
- 二進(jìn)制安全的數(shù)據(jù)結(jié)構(gòu)。 比如是操作命令是
get aaa\0
:獲取aaa\0
的值。如果是c語(yǔ)言的字符數(shù)組就會(huì)把\0
吞掉,變?yōu)?code>get aaa,而使用SDS
就會(huì)完整的操作aaa\0
,SDS
把所有接受到的數(shù)據(jù)都轉(zhuǎn)成字符串,即使是一些特殊字符! - SDS提供了內(nèi)存預(yù)分配機(jī)制,避免頻繁的內(nèi)存分配。 如果是c語(yǔ)言,在修改一個(gè)key時(shí),會(huì)分配一個(gè)新的字符數(shù)組,然后進(jìn)行內(nèi)存賦值,而SDS則采用預(yù)先分配機(jī)制,直接把字符串容量擴(kuò)大兩倍,key的長(zhǎng)度變化時(shí),直接在已分配的內(nèi)存中修改即可,如果不夠繼續(xù)擴(kuò)大2倍
思考:SDS采用的也是一種空間換時(shí)間的思路,無(wú)論是擴(kuò)展之后分配多余空間從而降低下次擴(kuò)展時(shí)需要再次內(nèi)存分配的概率,還是縮容之后并不立即回收空間而是留給下次擴(kuò)容,這兩種操作都會(huì)導(dǎo)致空閑空間增大,內(nèi)存占用提升,而Redis又了很多數(shù)據(jù)壓縮策略來(lái)控制內(nèi)存。 - 杜絕緩沖區(qū)溢出。 在C語(yǔ)言原生的字符串中,當(dāng)需要修改字符串且修改后的長(zhǎng)度大于修改前的長(zhǎng)度時(shí),在修改之前需要先對(duì)原數(shù)組申請(qǐng)空間擴(kuò)容,否則可能導(dǎo)致數(shù)組溢出,內(nèi)容寫入到相鄰的下一個(gè)數(shù)組中從而改變下一個(gè)字符串的值。
在SDS中,SDS屏蔽了用戶對(duì)數(shù)組空間的分配,SDS在增長(zhǎng)之前會(huì)根據(jù)free屬性自動(dòng)檢測(cè)是否足夠修改之后的字符串所需空間,如果足夠則直接修改,并更新修改之后的len和free屬性,如果當(dāng)前剩余空間不夠,SDS會(huì)根據(jù)空間分配策略自動(dòng)進(jìn)行擴(kuò)容,無(wú)需用戶關(guān)心。
思考:類似于Java中的String類,高級(jí)容器等,會(huì)提供自動(dòng)擴(kuò)容,縮容的功能,具體的細(xì)節(jié)對(duì)使用者透明,能減少開(kāi)發(fā)者的編碼負(fù)擔(dān)。
string+應(yīng)用場(chǎng)景
常用操作
命令 | 說(shuō)明 |
---|---|
SET key value | 存入字符串鍵值對(duì) |
MSET key value [key value …] | 批量存儲(chǔ)字符串鍵值對(duì) |
SETNX key value | 存入一個(gè)不存在的字符串鍵值對(duì) |
GET key | 獲取一個(gè)字符串鍵值 |
MGET key [key …] | 批量獲取字符串鍵值 |
EXPIRE key seconds | 設(shè)置一個(gè)鍵的過(guò)期時(shí)間(秒) |
DEL key [key …] | 刪除一個(gè)鍵 |
INCR key | 將key中儲(chǔ)存的數(shù)字值加1 |
DECR key | 將key中儲(chǔ)存的數(shù)字值減1 |
INCRBY key increment | 將key所儲(chǔ)存的值加上increment |
DECRBY key decrement | 將key所儲(chǔ)存的值減去decrement |
- 設(shè)置和獲取鍵值對(duì)
> SET key value
OK
> GET key
"value"
值得注意的是, 當(dāng)key存在時(shí),set命令會(huì)覆蓋掉你上一次設(shè)置的值
> SET key newValue
OK
> GET key
"newValue"
- 使用
EXISTS
和DEL
關(guān)鍵字來(lái)查詢是否存在和刪除鍵值對(duì):
> EXISTS key
(integer) 1
> DEL key
(integer) 1
> GET key
(nil)
- 批量設(shè)置鍵值對(duì)
> SET key1 value1
OK
> SET key2 value2
OK
> MGET key1 key2 key3 # 返回一個(gè)列表
1) "value1"
2) "value2"
3) (nil)
> MSET key1 value1 key2 value2
> MGET key1 key2
1) "value1"
2) "value2"
- 過(guò)期和 SET 命令擴(kuò)展
可以對(duì) key 設(shè)置過(guò)期時(shí)間,到時(shí)間會(huì)被自動(dòng)刪除,這個(gè)功能常用來(lái)控制緩存的失效時(shí)間。(過(guò)期可以是任意數(shù)據(jù)結(jié)構(gòu))
> SET key value1
> GET key
"value1"
> EXPIRE name 5 # 5s 后過(guò)期
... # 等待 5s
> GET key
(nil)
- 返回原值的 GETSET 命令
對(duì)字符串,還有一個(gè) GETSET 比較讓人覺(jué)得有意思,它的功能跟它名字一樣:為 key 設(shè)置一個(gè)值并返回原值:
> SET key value
> GETSET key value1
"value"
這可以對(duì)于某一些需要隔一段時(shí)間就統(tǒng)計(jì)的 key 很方便的設(shè)置和查看,例如:系統(tǒng)每當(dāng)由用戶進(jìn)入的時(shí)候你就是用 INCR
命令操作一個(gè) key,當(dāng)需要統(tǒng)計(jì)時(shí)候你就把這個(gè) key 使用 GETSET
命令重新賦值為 0,這樣就達(dá)到了統(tǒng)計(jì)的目的。
使用場(chǎng)景
- 單值緩存
SET key value
GET key
使用這兩條命令可以做用戶id存儲(chǔ)、商品庫(kù)存存儲(chǔ)等等!
- 對(duì)象存儲(chǔ)
以緩存user對(duì)象為例,有以下兩種方式:
(1)SET user:1 value(json格式數(shù)據(jù)):把對(duì)象轉(zhuǎn)json存入redis,也是當(dāng)下常用的方式,獲取數(shù)據(jù)需要做數(shù)據(jù)轉(zhuǎn)換
(2)MSET user:1:name zhuge user:1:balance 1888
MGET user:1:name user:1:balance
使用Mset命令,把對(duì)象拆開(kāi)存儲(chǔ),每一個(gè)key只保存對(duì)象的一個(gè)字段信息,適用于經(jīng)常修改user的某個(gè)字段的場(chǎng)景 - 分布式鎖
SETNX product:10001 true //操作product:10001
##執(zhí)行業(yè)務(wù)操作...
DEL product:10001 //刪除product:10001
其中SETNX key value 命令要求如果key已存在,則其他的setnx命令無(wú)法對(duì)當(dāng)前key進(jìn)行操作。
在使用分布式鎖時(shí)通常還會(huì)通過(guò)
SET product:10001 true ex 10 nx 命令設(shè)置key的超時(shí)時(shí)間,防止死鎖!
- 計(jì)數(shù)器
NCR 文章id
可以使用INCR命令實(shí)現(xiàn)數(shù)量自增,可以用于文章閱讀量、熱度人數(shù)統(tǒng)計(jì)等,用戶每點(diǎn)進(jìn)去一次執(zhí)行一次INCR命令
- 分布式系統(tǒng)全局序列號(hào)
在分布式系統(tǒng)下,如果需要分庫(kù)分表, mysql的數(shù)據(jù)庫(kù)自增id已經(jīng)無(wú)法滿足分庫(kù)分表下的id自增,這時(shí)就需要一個(gè)獨(dú)立于數(shù)據(jù)庫(kù)之外的中間件來(lái)實(shí)現(xiàn)id的分配。
redis的INCR命令可以實(shí)現(xiàn)id、序列號(hào)的生成,但如果用戶量非常大,每生成一個(gè)id、序列號(hào)都去redis會(huì)給redis添加不小的壓力,我們可以一次性從redis中自增1000次,把序列號(hào)放入本地內(nèi)存中,這1000個(gè)id用完了,再去redis再取1000個(gè),可有效降低redis的壓力!
hash+應(yīng)用場(chǎng)景
Redis 中的字典(hash)相當(dāng)于 Java 中的 HashMap,內(nèi)部實(shí)現(xiàn)也差不多類似,都是通過(guò) “數(shù)組 + 鏈表” 的鏈地址法來(lái)解決部分 哈希沖突,同時(shí)這樣的結(jié)構(gòu)也吸收了兩種不同數(shù)據(jù)結(jié)構(gòu)的優(yōu)點(diǎn)。
常用操作
命令 | 說(shuō)明 |
---|---|
HSET key field value | 存儲(chǔ)一個(gè)哈希表key的鍵值 |
HGET key field | 獲取哈希表key對(duì)應(yīng)的field鍵值 |
HMSET key field value [field value …] | 在一個(gè)哈希表key中存儲(chǔ)多個(gè)鍵值對(duì) |
HMGET key field [field …] | 批量獲取哈希表key中多個(gè)field鍵值 |
HSETNX key field value | 存儲(chǔ)一個(gè)不存在的哈希表key的鍵值 |
HDEL key field [field …] | 刪除哈希表key中的field鍵值 |
HLEN key | 返回哈希表key中field的數(shù)量 |
HGETALL key | 返回哈希表key中所有的鍵值 |
HINCRBY key field increment | 為哈希表key中field鍵的值加上增量increment |
字典相關(guān)操作
> HSET books java "think in java" # 命令行的字符串如果包含空格則需要使用引號(hào)包裹
(integer) 1
> HSET books python "python cookbook"
(integer) 1
> HGETALL books # key 和 value 間隔出現(xiàn)
1) "java"
2) "think in java"
3) "python"
4) "python cookbook"
> HGET books java
"think in java"
> HSET books java "head first java"
(integer) 0 # 因?yàn)槭歉虏僮?#xff0c;所以返回 0
> HMSET books java "effetive java" python "learning python" # 批量操作
OK
使用場(chǎng)景
- 電商購(gòu)物車
1)以用戶id為key === cart:1001
2)以商品id為field===10088
3)商品數(shù)量為value === 1
因此,購(gòu)物車操作可以如下:
1)添加商品:hset cart:1001 10088 1
2)增加數(shù)量hincrby cart:1001 10088 1
3)商品總數(shù)hlen cart:1001
4)刪除商品hdel cart:1001 10088
5)獲取購(gòu)物車所有商品hgetall cart:1001
思考-優(yōu)點(diǎn):
1)同類數(shù)據(jù)歸類整合儲(chǔ)存,方便數(shù)據(jù)管理
2)相比string操作消耗內(nèi)存與cpu更小
因?yàn)?#xff1a;string類型通過(guò)set key - val 的方式存儲(chǔ)數(shù)據(jù),通過(guò)對(duì)key進(jìn)行hash運(yùn)算決定當(dāng)前key是存儲(chǔ)在數(shù)組哪個(gè)位置。如果把hash類型的數(shù)據(jù)變成string類型來(lái)存儲(chǔ),則需要更多的key,同時(shí)在存放時(shí)也需要更多的hash(key)運(yùn)算,消耗更多的cpu資源!
3)相比string儲(chǔ)存更節(jié)省空間
如果把hash類型的數(shù)據(jù)變成string類型來(lái)存儲(chǔ),將需要存儲(chǔ)更多key,如果數(shù)據(jù)量很多的情況下,redis底層那么存儲(chǔ)數(shù)據(jù)的數(shù)組將很快會(huì)被占滿,占滿就會(huì)進(jìn)行擴(kuò)容,加大內(nèi)存消耗。由此可見(jiàn),string結(jié)構(gòu)與hash結(jié)構(gòu)只存儲(chǔ)一個(gè)key相比,需要更多的內(nèi)存空間!
思考-缺點(diǎn):
1)過(guò)期功能不能使用在field上,只能用在key上
redis的過(guò)期時(shí)間只能用在key上,而hash的key是一個(gè)大的概念,里面的map型結(jié)構(gòu)才是重要數(shù)據(jù),但過(guò)期時(shí)間只能用在外邊的大key上,hash結(jié)構(gòu)相比于string不能實(shí)現(xiàn)精準(zhǔn)過(guò)期!
2)hash結(jié)構(gòu)在Redis集群架構(gòu)下不適合大規(guī)模使用
因?yàn)槿绻粋€(gè)hash的key中的屬性很多的話,只能存在一個(gè)redis節(jié)點(diǎn)上,那么這個(gè)節(jié)點(diǎn)壓力會(huì)比其他節(jié)點(diǎn)壓力大很多,造成redis集群下壓力分配不均衡!
list+應(yīng)用場(chǎng)景
Redis 的列表相當(dāng)于 Java 語(yǔ)言中的 LinkedList,注意它是鏈表而不是數(shù)組。這意味著 list 的插入和刪除操作非常快,時(shí)間復(fù)雜度為 O(1),但是索引定位很慢,時(shí)間復(fù)雜度為 O(n)。
常用操作
命令 | 說(shuō)明 |
---|---|
LPUSH key value [value …] | 將一個(gè)或多個(gè)值value插入到key列表的表頭(最左邊) |
RPUSH key value [value …] | 將一個(gè)或多個(gè)值value插入到key列表的表尾(最右邊) |
LPOP key | 移除并返回key列表的頭元素 |
RPOP key | 移除并返回key列表的尾元素 |
LRANGE key start stop | 返回列表key中指定區(qū)間內(nèi)的元素,區(qū)間以偏移量start和stop指定 |
BLPOP key [key …] timeout | 從key列表表頭彈出一個(gè)元素,若列表中沒(méi)有元素,阻塞等待 |
timeout:秒,如果timeout=0,一直阻塞等待 | |
BRPOP key [key …] timeout | 從key列表表尾彈出一個(gè)元素,若列表中沒(méi)有元素,阻塞等待 |
timeout:秒,如果timeout=0,一直阻塞等待 |
list相關(guān)
LPUSH
和RPUSH
分別可以向list的左邊(頭部)和右邊(尾部)添加一個(gè)新元素LRANGE
命令可以從list中取出一定范圍的元素LINDEX
命令可以從list中取出指定下標(biāo)的元素,相當(dāng)于java量表操作中的get(int index)
操作
> rpush mylist A
(integer) 1
> rpush mylist B
(integer) 2
> lpush mylist first
(integer) 3
> lrange mylist 0 -1 # -1 表示倒數(shù)第一個(gè)元素, 這里表示從第一個(gè)元素到最后一個(gè)元素,即所有
1) "first"
2) "A"
3) "B"
- list 實(shí)現(xiàn)隊(duì)列
隊(duì)列是先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),常用于消息排隊(duì)和異步邏輯處理,它會(huì)確保元素的訪問(wèn)順序:
> RPUSH books python java golang
(integer) 3
> LPOP books
"python"
> LPOP books
"java"
> LPOP books
"golang"
> LPOP books
(nil)
- list 實(shí)現(xiàn)棧
棧是先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),跟隊(duì)列正好相反
> RPUSH books python java golang
> RPOP books
"golang"
> RPOP books
"java"
> RPOP books
"python"
> RPOP books
(nil)
list的使用場(chǎng)景
- 模擬分布式系統(tǒng)數(shù)據(jù)結(jié)構(gòu)
①:Stack(棧) = LPUSH(左邊放) + LPOP(左邊取)
②:Queue(隊(duì)列)= LPUSH(左邊放) + RPOP(右邊取)
③:Blocking MQ(阻塞隊(duì)列)= LPUSH(左邊放) + BRPOP(右邊阻塞取:沒(méi)有數(shù)據(jù)就阻塞!)
思考:那么redis實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)和jdk中提供的數(shù)據(jù)結(jié)構(gòu)有什么區(qū)別呢?
答:jdk提供的數(shù)據(jù)結(jié)構(gòu)僅在本服務(wù)中有用,如果在分布式環(huán)境下,則需要借助redis等中間件,模擬數(shù)據(jù)結(jié)構(gòu)來(lái)統(tǒng)一管理數(shù)據(jù)。 - 微博、朋友圈、公眾號(hào)等,關(guān)注的文章列表展示
假如 小明 關(guān)注了 中國(guó)青年報(bào)、三太子敖丙 等大V的訂閱號(hào),當(dāng)這些大V發(fā)布訂閱號(hào)時(shí),通過(guò)推或拉的方式把消息LPUSH放入redis中屬于小明的list中。其中key為msg:{小明_ID}。當(dāng)小明要獲取大V們發(fā)的消息時(shí),使用LRANGE 命令從隊(duì)列中獲取指定個(gè)數(shù)的訂閱號(hào)信息!!
# ①:MacTalk發(fā)微博,消息ID為10010
LPUSH msg:{小明_ID} 10010# ②:備胎說(shuō)車發(fā)微博,消息ID為10086
LPUSH msg:{小明_ID} 10086# ③:查看最新微博消息(前4條)
LRANGE msg:{小明_ID} 0 4
思考:大V發(fā)了消息的是怎么存儲(chǔ)在粉絲的redis中呢?一般有兩種處理方案!
(1)推送,博主發(fā)了消息,通過(guò)線程先推送到在線粉絲的隊(duì)列中,其他不在線的粉絲等后面系統(tǒng)在空閑的時(shí)候再慢慢推送過(guò)去
(2)拉取,如果粉絲太多,推的方案還是要很長(zhǎng)時(shí)間去處理,還有一種方案就是拉,每一個(gè)粉絲上線后就去關(guān)注的博主那里拉取他發(fā)送的最新的消息,在使用LRANGE
取出即可。
set+應(yīng)用場(chǎng)景
Redis 的集合相當(dāng)于 Java 語(yǔ)言中的 HashSet,它內(nèi)部的鍵值對(duì)是無(wú)序、唯一的。它的內(nèi)部實(shí)現(xiàn)相當(dāng)于一個(gè)特殊的字典,字典中所有的 value 都是一個(gè)值 NULL。
set 的常用操作
命令 | 說(shuō)明 |
---|---|
SADD key member [member …] | 往集合key中存入元素,元素存在則忽略,若key不存在則新建 |
SREM key member [member …] | 從集合key中刪除元素 |
SMEMBERS key | 獲取集合key中所有元素 |
SCARD key | 獲取集合key的元素個(gè)數(shù) |
SISMEMBER key member | 判斷member元素是否存在于集合key中 |
SRANDMEMBER key [count] | 從集合key中選出count個(gè)元素,元素不從key中刪除 |
SPOP key [count] | 從集合key中選出count個(gè)元素,元素從key中刪除 |
set 的運(yùn)算操作
命令 | 說(shuō)明 |
---|---|
SINTER key [key …] | 交集運(yùn)算 |
SINTERSTORE destination key [key …] | 將交集結(jié)果存入新集合destination中 |
SUNION key [key …] | 并集運(yùn)算 |
SUNIONSTORE destination key [key …] | 將并集結(jié)果存入新集合destination中 |
SDIFF key [key …] | 差集運(yùn)算 |
SDIFFSTORE destination key [key …] | SDIFFSTORE destination key [key …] |
集合set的基本使用
> SADD books java
(integer) 1
> SADD books java # 重復(fù)
(integer) 0
> SADD books python golang
(integer) 2
> SMEMBERS books # 注意順序,set 是無(wú)序的
1) "java"
2) "python"
3) "golang"
> SISMEMBER books java # 查詢某個(gè) value 是否存在,相當(dāng)于 contains
(integer) 1
> SCARD books # 獲取長(zhǎng)度
(integer) 3
> SPOP books # 彈出一個(gè)
"java"
set的使用場(chǎng)景
- 抽獎(jiǎng)活動(dòng)
1)點(diǎn)擊參與抽獎(jiǎng)加入集合
SADD key {userlD}
2)查看參與抽獎(jiǎng)所有用戶
SMEMBERS key
3)隨機(jī)抽取count名中獎(jiǎng)?wù)?br /> SRANDMEMBER key [count] ------元素不從集合中刪除
SPOP key [count] ------ 元素從集合中刪除 - 朋友圈點(diǎn)贊
當(dāng)某人在朋友圈發(fā)布消息,可用set來(lái)點(diǎn)贊展示
1)點(diǎn)贊
SADD like:{消息ID} {用戶ID}
2)取消點(diǎn)贊
SREM like:{消息ID} {用戶ID}
3)檢查用戶是否點(diǎn)過(guò)贊
SISMEMBER like:{消息ID} {用戶ID}
4)獲取點(diǎn)贊的用戶列表
SMEMBERS like:{消息ID}
5)獲取點(diǎn)贊用戶數(shù)
SCARD like:{消息ID} - 利用set的交、并、差集實(shí)現(xiàn)微博、微信關(guān)注模型
關(guān)注模型如下圖:
首先了解一下set的集合操作,假如有三個(gè)集合
set1:(a、b、c)
set2:(b、c、d)
set3:(c、d、e)
三個(gè)集合的
交集為:SINTER set1 set2 set3 ==> { c }
并集為:SUNION set1 set2 set3 ==> { a,b,c,d,e }
差集為:SDIFF set1 set2 set3 ==> { a }
差集計(jì)算方式:set1 - (set2并set3) = {a、b、c} - {b、c、d、e} = {a} 只保留a中單獨(dú)存在的元素
共同關(guān)注A的人:可以用交集來(lái)實(shí)現(xiàn)
我可能認(rèn)識(shí)的人:可以使用差集來(lái)實(shí)現(xiàn),把我的好友求差集,例如a的好友={b,c},b的好友={a,c,d},那么a可能認(rèn)識(shí)的人可以是,b-a={a,b,c,d}-{a,b,c} = vxwlu0yf4。當(dāng)然這只是一個(gè)簡(jiǎn)單又不大現(xiàn)實(shí)的想法,相當(dāng)于把對(duì)方的好友全部推薦給你了,真實(shí)情況的還需要考慮到關(guān)系網(wǎng)絡(luò),好友的權(quán)重?cái)?shù)等
zset+應(yīng)用場(chǎng)景
這可能使 Redis 最具特色的一個(gè)數(shù)據(jù)結(jié)構(gòu)了,它類似于 Java 中 SortedSet 和 HashMap 的結(jié)合體,一方面它是一個(gè) set,保證了內(nèi)部 value 的唯一性,另一方面它可以為每個(gè) value 賦予一個(gè) score 值,用來(lái)代表排序的權(quán)重。
zset相比于set多一個(gè)score 分值,正是根據(jù)這個(gè)分值進(jìn)行排序,所以zset才能展示有序的數(shù)據(jù)
zset 的常用操作
命令 | 說(shuō)明 |
---|---|
ZADD key score member [[score member]…] | 往有序集合key中加入帶分值元素 |
ZREM key member [member …] | 從有序集合key中刪除元素 |
ZSCORE key member | 返回有序集合key中元素member的分值 |
ZINCRBY key increment member | 為有序集合key中元素member的分值加上increment |
ZCARD key | 返回有序集合key中元素個(gè)數(shù) |
ZRANGE key start stop [WITHSCORES] | 正序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素 |
ZREVRANGE key start stop [WITHSCORES] | 倒序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素 |
zset的基本使用
> ZADD books 9.0 "think in java"
> ZADD books 8.9 "java concurrency"
> ZADD books 8.6 "java cookbook"> ZRANGE books 0 -1 # 按 score 排序列出,參數(shù)區(qū)間為排名范圍
1) "java cookbook"
2) "java concurrency"
3) "think in java"> ZREVRANGE books 0 -1 # 按 score 逆序列出,參數(shù)區(qū)間為排名范圍
1) "think in java"
2) "java concurrency"
3) "java cookbook"> ZCARD books # 相當(dāng)于 count()
(integer) 3> ZSCORE books "java concurrency" # 獲取指定 value 的 score
"8.9000000000000004" # 內(nèi)部 score 使用 double 類型進(jìn)行存儲(chǔ),所以存在小數(shù)點(diǎn)精度問(wèn)題> ZRANK books "java concurrency" # 排名
(integer) 1> ZRANGEBYSCORE books 0 8.91 # 根據(jù)分值區(qū)間遍歷 zset
1) "java cookbook"
2) "java concurrency"> ZRANGEBYSCORE books -inf 8.91 withscores # 根據(jù)分值區(qū)間 (-∞, 8.91] 遍歷 zset,同時(shí)返回分值。inf 代表 infinite,無(wú)窮大的意思。
1) "java cookbook"
2) "8.5999999999999996"
3) "java concurrency"
4) "8.9000000000000004"> ZREM books "java concurrency" # 刪除 value
(integer) 1
> ZRANGE books 0 -1
1) "java cookbook"
2) "think in java
zset應(yīng)用場(chǎng)景
- 實(shí)現(xiàn)熱搜排行榜
①:點(diǎn)擊 “國(guó)慶放假” 新聞時(shí),為其分值+1
ZINCRBY hotNews:20190819 1 國(guó)慶放假
②:展示當(dāng)日排行前十
ZREVRANGE hotNews:20190819 0 9 WITHSCORES
③:七日搜索榜單計(jì)算
取7天的key求并集放入新的key=hotNews:20190813-20190819中,就得出這7天中的訪問(wèn)量排行榜
ZUNIONSTORE hotNews:20190813-20190819 7
hotNews:20190813 hotNews:20190814… hotNews:20190819
④:展示七日排行前十
根據(jù)上邊的并集,從新的key=hotNews:20190813-20190819中取出前10名
ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES