您好,登錄后才能下訂單哦!
本篇文章為大家展示了PHP+Redis有序集合如何實(shí)現(xiàn) 24 小時(shí)排行榜實(shí)時(shí)更新,代碼簡明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
基本介紹
Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復(fù)的成員。
不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) double 類型的分?jǐn)?shù)。redis 正是通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序。
有序集合的成員是唯一的,但分?jǐn)?shù) (score) 卻可以重復(fù)。
集合是通過哈希表實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是 O (1)。 集合中最大的成員數(shù)為 2^32 - 1^ (4294967295, 每個(gè)集合可存儲(chǔ) 40 多億個(gè)成員)。
有序集合首先是集合,其成員(member)具有唯一性,其次,每個(gè)成員關(guān)聯(lián)了一個(gè)分?jǐn)?shù)(score),使得成員可以按照分?jǐn)?shù)排序。
需求描述
設(shè)想在一個(gè)游戲中,有上百萬的玩家數(shù)據(jù),如果現(xiàn)在需要你根據(jù)玩家的經(jīng)驗(yàn)值整理一個(gè)前 10 名的排行榜,你會(huì)怎么做呢?一般的做法是寫一條類似下面這條 sql 語句的方式來獲?。?/p>
select * from game_socre order by score desc limit 0,20
這種方式在數(shù)據(jù)量較小的情況下可行,但是在數(shù)據(jù)量大的情況下查詢速度將變慢,特別是還需要聯(lián)表查詢時(shí),速度下降的就更明顯了。
實(shí)現(xiàn)
這時(shí)你可以考慮使用 redis 來實(shí)現(xiàn)這個(gè)功能。
實(shí)現(xiàn)這個(gè)功能主要用到的 redis 數(shù)據(jù)類型是 redis 的有序集合 zset。zset 是 set 類型的一個(gè)擴(kuò)展,比原有的類型多了一個(gè)順序?qū)傩?。此屬性在每次插入?shù)據(jù)時(shí)會(huì)自動(dòng)調(diào)整順序值,保證 value 值按照一定順序連續(xù)排列。
主要的實(shí)現(xiàn)思路是:
1、在一個(gè)新的玩家參與到游戲中時(shí),在 redis 中的 zset 中新增一條記錄(記錄內(nèi)容看具體的需求)score 為 0
2、當(dāng)玩家的經(jīng)驗(yàn)值發(fā)生變化時(shí),修改該玩家的 score 值
3、使用 redis 的 ZREVRANGE 方法獲取排行榜
返回有序集 key 中,指定區(qū)間內(nèi)的成員。其中成員的位置按 score 值遞減 (從大到小) 來排列。具有相同 score 值的成員按字典序的反序排列。 除了成員按 score 值遞減的次序排列這一點(diǎn)外,ZREVRANGE 命令的其他方面和 ZRANGE 命令一樣。
redis 127.0.0.1:6379> ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN
1、數(shù)據(jù)準(zhǔn)備
2、獲取 score 高分 top10 排名 (ZREVRANGE 為降序,ZRANGE 為升序)
3、查看用戶 ee 的實(shí)際排名 (ZREVRANK 為降序,ZRANK 為升序)、實(shí)時(shí)分?jǐn)?shù)
進(jìn)一步需求
需要實(shí)現(xiàn)最近的 24 小時(shí)用戶積分排行榜,并統(tǒng)計(jì)前 10 名的玩家和積分
實(shí)現(xiàn)
主要的實(shí)現(xiàn)思路是:
利用 ZADD 按小時(shí)劃分添加用戶的積分信息,然后用 ZUNIONSTORE 并集實(shí)現(xiàn) 24 小時(shí)的游戲積分總和,實(shí)現(xiàn) “24 小時(shí)排行榜”;(如果有更好的思路,能夠在下方留言不吝賜教一下就更好了)
ZUNIONSTORE destination numkeys key [key ...]
Redis Zunionstore 命令計(jì)算給定的一個(gè)或多個(gè)有序集的并集,其中給定 key 的數(shù)量必須以 numkeys 參數(shù)指定,并 將該并集(結(jié)果集)儲(chǔ)存到 destination 。
默認(rèn)情況下,結(jié)果集中某個(gè)成員的分?jǐn)?shù)值是所有給定集下該成員分?jǐn)?shù)值之和 。
可能碰到的問題
1、相同分?jǐn)?shù)問題
Redis 在遇到分?jǐn)?shù)相同時(shí)是按照集合成員自身的字典順序來排序,這里即是按照”user2″和”user3″這兩個(gè)字符串進(jìn)行排序,以逆序排序的話 user3 自然排到了前面。要解決這個(gè)問題,我們可以考慮在分?jǐn)?shù)中加入時(shí)間戳,計(jì)算公式為:
帶時(shí)間戳的分?jǐn)?shù) = 實(shí)際分?jǐn)?shù)*10000000000 + (9999999999 – timestamp)
timestamp 我們采用系統(tǒng)提供的 time () 函數(shù),也就是 1970 年 1 月 1 日以來的秒數(shù),我們采用 32 位的時(shí)間戳(這能堅(jiān)持到 2038 年),由于 32 位時(shí)間戳是 10 位十進(jìn)制整數(shù)(最大值 4294967295),所以我們讓時(shí)間戳占據(jù)低 10 位(十進(jìn)制整數(shù)),實(shí)際分?jǐn)?shù)則擴(kuò)大 10^10 倍,然后把兩部分相加的結(jié)果作為 zset 的分?jǐn)?shù)??紤]到要按時(shí)間倒序排列,所以時(shí)間戳這部分需要顛倒一下,這便是用 9999999999 減去時(shí)間戳的原因。當(dāng)我們要讀取玩家實(shí)際分?jǐn)?shù)時(shí),只需去掉后 10 位即可。
初步看起來這個(gè)方案還不錯(cuò),但這里面有兩個(gè)問題。
第一個(gè)問題是小問題,采用秒為時(shí)間戳可能區(qū)分度還不夠,如果同一秒出現(xiàn)兩個(gè)分?jǐn)?shù)相同的仍然會(huì)出現(xiàn)前面的問題,當(dāng)然我們可以選擇精度更高的時(shí)間戳,但在實(shí)際場景中,同一秒誰排前面已經(jīng)無關(guān)緊要。
第二個(gè)問題是大問題,因?yàn)?Redis 的分?jǐn)?shù)類型采用的是 double,64 位雙精度浮點(diǎn)數(shù)只有 52 位有效數(shù)字,它能精確表達(dá)的整數(shù)范圍為 - 2^53 到 2^53,最高只能表示 16 位十進(jìn)制整數(shù)(最大值為 9007199254740992,其實(shí)連 16 位也不能完整表示)。這就是說,如果前面時(shí)間戳占了 10 位的話,分?jǐn)?shù)就只剩下 6 位了,這對(duì)于某些排行榜分?jǐn)?shù)來說是不夠用的。我們可以考慮縮減時(shí)間戳位數(shù),比如從 2015 年 1 月 1 日開始計(jì)時(shí),但這仍然增加不了幾位?;蛘邷p少區(qū)分度,以分鐘、小時(shí)來作為時(shí)間戳單位。
如果 Redis 的分?jǐn)?shù)類型為 int64,我們就沒有上面的煩惱。說到這里,其實(shí) Redis 真應(yīng)該再額外提供一個(gè) int64 類型的 ZSet,但目前只能是幻想,除非自己改其源碼。
上述內(nèi)容就是PHP+Redis有序集合如何實(shí)現(xiàn) 24 小時(shí)排行榜實(shí)時(shí)更新,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。