Redis有序集合zset應(yīng)用場(chǎng)景

一捉捅、zset(sorted set:有序集合)

Redis zset和Set一樣也是String類型元素的集合仆葡,且不允許重復(fù)的成員绘闷。不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類型的分?jǐn)?shù)忆肾。Redis正是通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序室梅。zset的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。

Redis zset類似Java里的LinkedSet(不重復(fù)有序)疚宇,當(dāng)zset中最后一個(gè)value 被移除后亡鼠,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除,內(nèi)存被回收敷待。

二间涵、應(yīng)用場(chǎng)景

2.1 成績(jī)榜單
2.1.1 成績(jī)表單添加數(shù)據(jù)

ZADD命令:添加or更新成員分?jǐn)?shù)

//命令參數(shù)
ZADD key score member [[score member] [score member] ...]

將一個(gè)或多個(gè)member元素及其score值加入到有序集key當(dāng)中。如果某個(gè)member已經(jīng)是有序集的成員榜揖,那么更新這個(gè)member的score值勾哩,并通過重新插入這個(gè)member元素顶捷,來保證該member在正確的位置上牡辽。

score值可以是整數(shù)值或雙精度浮點(diǎn)數(shù)。如果key不存在棒坏,則創(chuàng)建一個(gè)空的有序集并執(zhí)行ZADD操作妨猩。當(dāng)key存在但不是有序集類型時(shí)潜叛,返回一個(gè)錯(cuò)誤。

//假設(shè)用戶A(user1)當(dāng)前游戲的分?jǐn)?shù)為50壶硅,則
ZADD user_rank 50 user1

//添加用戶B(user2)當(dāng)前游戲的分?jǐn)?shù)為60威兜、用戶C(user3)當(dāng)前游戲的分?jǐn)?shù)為70,則可批量操作庐椒,同時(shí)添加user2椒舵、user3 兩個(gè)用戶的分?jǐn)?shù)
ZADD user_rank 60 user2 70 user3
2.1.2 ZREVRANK 獲取成員當(dāng)前的排名
//命令參數(shù)
ZREVRANK key member

返回有序集key中成員member的排名,其中有序集成員按score值遞減(從大到小)排序约谈。排名以0為底笔宿,也就是說犁钟,score 值最大的成員排名為0 。

//獲取用戶A當(dāng)前的排名措伐,user1 當(dāng)前排名為第三特纤,則輸出2
ZREVRANK user_rank user1
2.2.3 ZSCORE 獲取用戶分?jǐn)?shù)(排序值)
//命令參數(shù)
ZSCORE key member

返回有序集key中,成員member的score值侥加。如果member元素不是有序集key的成員或key不存在返回nil捧存。

// 獲取用戶A當(dāng)前的分?jǐn)?shù),user1當(dāng)前分?jǐn)?shù)為50担败,則輸出"50"昔穴,注意返回值是字符串
ZSCORE user_rank user1
2.2 游戲排行榜

一個(gè)典型的游戲排行榜包括以下常見功能:

  • 能夠記錄每個(gè)玩家的分?jǐn)?shù);
  • 能夠?qū)ν婕业姆謹(jǐn)?shù)進(jìn)行更新提前;
  • 能夠查詢每個(gè)玩家的分?jǐn)?shù)和名次吗货;
  • 能夠按名次查詢排名前N名的玩家;
  • 能夠查詢排在指定玩家前后M名的玩家狈网。

更進(jìn)一步宙搬,上面的操作都需要在短時(shí)間內(nèi)實(shí)時(shí)完成,這樣才能最大程度發(fā)揮排行榜的效用拓哺。由于一個(gè)玩家名次上升x位將會(huì)引起x+1位玩家的名次發(fā)生變化(包括該玩家)勇垛,如果采用傳統(tǒng)數(shù)據(jù)庫(kù)(比如MySQL)來實(shí)現(xiàn)排行榜,當(dāng)玩家人數(shù)較多時(shí)士鸥,將會(huì)導(dǎo)致對(duì)數(shù)據(jù)庫(kù)的頻繁修改闲孤,性能得不到滿足,所以不合適烤礁。

2.2.1 zadd設(shè)置玩家分?jǐn)?shù)

下面設(shè)置了4個(gè)玩家的分?jǐn)?shù)讼积,如果玩家分?jǐn)?shù)已經(jīng)存在,則會(huì)覆蓋之前的分?jǐn)?shù)脚仔。

> redis 127.0.0.1:6379> zadd lb 89 user1
> (integer) 1
> redis 127.0.0.1:6379> zadd lb 95 user2
> (integer) 1
> redis 127.0.0.1:6379> zadd lb 95 user3
> (integer) 1
> redis 127.0.0.1:6379> zadd lb 90 user4
> (integer) 1
2.2.2 zscore查看玩家分?jǐn)?shù)

下面是查看user2這個(gè)玩家在lb排行榜中的分?jǐn)?shù)勤众。

redis 127.0.0.1:6379> zscore lb user2 “95”
2.2.3 zrevrange按名次查看排行榜

由于排行榜一般是按照分?jǐn)?shù)由高到低排序的,所以我們使用zrevrange鲤脏,而命令zrange是按照分?jǐn)?shù)由低到高排序决摧。起始位置和結(jié)束位置都是以0開始的索引,且都包含在內(nèi)凑兰。如果結(jié)束位置為-1則查看范圍為整個(gè)排行榜掌桩。帶上withscores則會(huì)返回玩家分?jǐn)?shù)。下面為查看所有玩家分?jǐn)?shù):

> redis 127.0.0.1:6379> zrevrange lb 0 -1 withscores
> 1) “user3”
> 2) “95”
> 3) “user2”
> 4) “95”
> 5) “user4”
> 6) “90”
> 7) “user1”
> 8) “89”

下面為查詢前三名玩家分?jǐn)?shù):

> redis 127.0.0.1:6379> zrevrange lb 0 2 withscores
> 1) “user3”
> 2) “95”
> 3) “user2”
> 4) “95”
> 5) “user4”
> 6) “90”
2.2.4 zrevrank查看玩家的排名

與zrevrange類似姑食,zrevrank是以分?jǐn)?shù)由高到低的排序返回玩家排名(實(shí)際返回的是以0開始的索引)波岛,對(duì)應(yīng)的zrank則是以分?jǐn)?shù)由低到高的排序返回排名。下面是查詢玩家user3和user4的排名:

> redis 127.0.0.1:6379> zrevrank lb user3
> (integer) 0
> redis 127.0.0.1:6379> zrevrank lb user1
> (integer) 3
2.2.5 zincrby增減玩家分?jǐn)?shù)

有的排行榜是在變更時(shí)重新設(shè)置玩家的分?jǐn)?shù)音半,而還有的排行榜則是以增量方式修改玩家分?jǐn)?shù)则拷,增量可正可負(fù)贡蓖。如果執(zhí)行zincrby時(shí)玩家尚不在排行榜中,則認(rèn)為其原始分?jǐn)?shù)為0煌茬,相當(dāng)于執(zhí)行zdd斥铺。下面將user4的分?jǐn)?shù)增加6,使其名次上升到第一位坛善。

> redis 127.0.0.1:6379> zincrby lb 6 user4
> “96”
> redis 127.0.0.1:6379> zrevrange lb 0 -1 withscores
> 1) “user4”
> 2) “96”
> 3) “user3”
> 4) “95”
> 5) “user2”
> 6) “95”
> 7) “user1”
> 8) “89”
2.2.6 zrem移除某個(gè)玩家

下面移除玩家user4

> redis 127.0.0.1:6379> zrem lb user4
> (integer) 1
> redis 127.0.0.1:6379> zrevrange lb 0 -1 withscores
> 1) “user3”
> 2) “95”
> 3) “user2”
> 4) “95”
> 5) “user1”
> 6) “89”
2.2.7 del刪除排行榜

排行榜對(duì)象在我們首次調(diào)用zadd或zincrby時(shí)被創(chuàng)建晾蜘,當(dāng)我們要?jiǎng)h除它時(shí),調(diào)用redis通用的命令del即可眠屎。

> redis 127.0.0.1:6379> del lb
> (integer) 1
> redis 127.0.0.1:6379> get lb
> (nil)
2.2.8 相同分?jǐn)?shù)問題

從前面的例子我們可以看到剔交,user2和user3具有相同的分?jǐn)?shù),但在按分?jǐn)?shù)逆序排序時(shí)改衩,user3排在了user2前面岖常。而在實(shí)際應(yīng)用場(chǎng)景中,我們更希望看到user2排在user3前面葫督,因?yàn)閡ser2比user3先加入排行榜竭鞍,也就是說user2先到達(dá)該分?jǐn)?shù)。但Redis在遇到分?jǐn)?shù)相同時(shí)是按照集合成員自身的字典順序來排序橄镜,這里即是按照”user2″和”user3″這兩個(gè)字符串進(jìn)行排序偎快,以逆序排序的話user3自然排到了前面。要解決這個(gè)問題蛉鹿,我們可以考慮在分?jǐn)?shù)中加入時(shí)間戳滨砍。

2.3 微信步數(shù)排行榜

本文基于微信步數(shù)排行榜介紹如何使用Redis的ZSET實(shí)現(xiàn)排行榜往湿,重點(diǎn)是Redis的ZSET的使用妖异,因此只是分析了微信步數(shù)排行榜的大致實(shí)現(xiàn)思路,實(shí)際實(shí)現(xiàn)肯定比文中分析的復(fù)雜的多领追。首先他膳,我們來分析下微信步數(shù)排行榜的需求:

  • 排行榜是以日期為單位的,歷史日期的排行榜是可以查看的

  • 排行榜可能并不會(huì)顯示所有好友的步數(shù)绒窑,比如我的微信有349位好友棕孙,但排行榜從來沒有顯示過這么多,假設(shè)最多只顯示步數(shù)前200的好友

  • 步數(shù)是異步更新的些膨,所以每隔一段時(shí)間步數(shù)同步后蟀俊,排行榜都會(huì)變化

  • 排行榜中,好友頭像和微信昵稱可以理解為不變的(變動(dòng)的幾率小订雾,就像熱搜榜中的標(biāo)題和Url)肢预,但步數(shù)和點(diǎn)贊數(shù)是可變的

基于以上分析的需求,大致實(shí)現(xiàn)思路如下:
1洼哎、使用Redis的ZSET數(shù)據(jù)結(jié)構(gòu)

2烫映、設(shè)置key時(shí)沼本,基于微信號(hào)和日期,比如我的微信是zwwhnly锭沟,今天的日期是2020-06-01抽兆,那么key就可以設(shè)計(jì)為:StepNumberRanking:zwwhnly:20200601

3、設(shè)置value時(shí)族淮,將好友的昵稱作為成員member辫红,將好友的步數(shù)作為分值score,如下所示:

1

4瞧筛、使用Redis的HASH數(shù)據(jù)結(jié)構(gòu)厉熟,其中key為第2步的key+第3步的成員member,value分別存儲(chǔ)好友頭像较幌、昵稱揍瑟、步數(shù)、點(diǎn)贊數(shù)乍炉,如下所示:

2

5绢片、獲取微信步數(shù)排行榜時(shí),分為以下2步:

A岛琼、先查詢出微信步數(shù)排行榜中的好友昵稱底循,也就是查詢

StepNumberRanking:zwwhnly:20200601的值

B、根據(jù)獲取到的好友昵稱槐瑞,查詢好友步數(shù)信息熙涤,也就是查詢

StepNumberRanking:zwwhnly:20200601:yst的值
2.3.1 ZADD初始化微信步數(shù)排行榜

執(zhí)行如下命令初始化微信步數(shù)排行榜,以上面圖片中的9個(gè)好友為例困檩,分2次初始化:

ZADD StepNumberRanking:zwwhnly:20200602 25452 yst 23683 zq 23599 ljx 20391 yyq 19628 XxZz

ZADD StepNumberRanking:zwwhnly:20200602 18261 lxx 16636 zcc 16555 clc 16098 fl

執(zhí)行完的效果如下圖所示:

3

可以看到祠挫,默認(rèn)是以score正序排列的,也就是步數(shù)從少到多排列悼沿。

2.3.2 HMSET存用戶詳細(xì)信息

因?yàn)檎故静綌?shù)排行榜時(shí)等舔,需要展示昵稱、頭像糟趾、步數(shù)慌植、點(diǎn)贊數(shù),所以可以借助于Redis的HASH 數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)义郑,這時(shí)就要用到HMSET命令

4
2.3.3 ZINCRBY更新好友步數(shù)

每隔一段時(shí)間蝶柿,好友的步數(shù)是會(huì)更新的,此時(shí)可以使用ZINCRBY命令來更新好友步數(shù)非驮,假設(shè)我們只更新步數(shù)位于前2位好友的步數(shù)交汤,給他們的步數(shù)增加10,就可以執(zhí)行以下命令:

ZINCRBY StepNumberRanking:zwwhnly:20200602 10 yst ZINCRBY StepNumberRanking:zwwhnly:20200602 10 zq

執(zhí)行完的效果如下圖所示:

5

更新完排行榜里的步數(shù)后院尔,不要忘記執(zhí)行HMSET命令更新好友的步數(shù):

6
2.3.4 ZRANGE正序

在所有的數(shù)據(jù)就緒后蜻展,剩下的就是查詢了喉誊,我們可以使用ZRANGE命令獲取排行榜里的好友信息:

ZRANGE StepNumberRanking:zwwhnly:20200602 0 -1
7

可以看出,查詢出的好友信息是按步數(shù)從少到多排序的纵顾,而排行榜應(yīng)該按步數(shù)從多到少排序伍茄,這就用到了下面的ZREVRANGE命令。

2.3.5 ZREVRANGE倒序

ZREVRANGE命令和ZRANGE命令類似施逾,不過是按score倒序的敷矫,剛好符合排行榜的場(chǎng)景。

ZREVRANGE StepNumberRanking:zwwhnly:20200602 0 -1 WITHSCORES
7

可以看出汉额,查詢出的好友信息按步數(shù)從大到小排序曹仗,剛好就是在排行榜要展示的順序。不過蠕搜,排行榜一般都不展示所有的數(shù)據(jù)怎茫,這里我們的數(shù)據(jù)比較少,如果只獲取步數(shù)top5的好友妓灌,就可以執(zhí)行如下命令:

ZREVRANGE StepNumberRanking:zwwhnly:20200602 0 4 WITHSCORES
8

如果你要獲取top200轨蛤,就將上面的4修改為199。

2.3.6 HGETALL獲取好友詳情

獲取到了排行榜里的好友信息虫埂,最后一步就是獲取這些好友的步數(shù)祥山、點(diǎn)贊數(shù)、頭像掉伏、昵稱這些信息缝呕,也就是我們之前使用HASH數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)的信息,此時(shí)我們可以使用HGETALL命令斧散,如下所示:

HGETALL StepNumberRanking:zwwhnly:20200602:yst
9

資料來源:
使用Redis的有序集合實(shí)現(xiàn)排行榜功能
Redis場(chǎng)景應(yīng)用之排行榜
使用redis進(jìn)行排行榜的小秘訣

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末供常,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颅湘,更是在濱河造成了極大的恐慌话侧,老刑警劉巖栗精,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闯参,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡悲立,警方通過查閱死者的電腦和手機(jī)鹿寨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薪夕,“玉大人脚草,你說我怎么就攤上這事≡祝” “怎么了馏慨?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵埂淮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我写隶,道長(zhǎng)倔撞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任慕趴,我火速辦了婚禮痪蝇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冕房。我一直安慰自己躏啰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布耙册。 她就那樣靜靜地躺著给僵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪详拙。 梳的紋絲不亂的頭發(fā)上想际,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音溪厘,去河邊找鬼胡本。 笑死,一個(gè)胖子當(dāng)著我的面吹牛畸悬,可吹牛的內(nèi)容都是我干的侧甫。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蹋宦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼披粟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冷冗,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤守屉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蒿辙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拇泛,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年思灌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俺叭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泰偿,死狀恐怖熄守,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤裕照,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布攒发,位于F島的核電站,受9級(jí)特大地震影響晋南,放射性物質(zhì)發(fā)生泄漏晨继。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一搬俊、第九天 我趴在偏房一處隱蔽的房頂上張望紊扬。 院中可真熱鬧,春花似錦唉擂、人聲如沸餐屎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腹缩。三九已至,卻和暖如春空扎,著一層夾襖步出監(jiān)牢的瞬間藏鹊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工转锈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盘寡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓撮慨,卻偏偏與公主長(zhǎng)得像竿痰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砌溺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容