欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis實(shí)現(xiàn)排行榜及相同積分按時(shí)間排序功能的實(shí)現(xiàn)

 更新時(shí)間:2022年08月22日 11:11:05   作者:morris131  
這篇文章主要介紹了Redis實(shí)現(xiàn)排行榜及相同積分按時(shí)間排序,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

在日常的開(kāi)發(fā)中,經(jīng)常會(huì)碰到需要對(duì)用戶(hù)的分值等進(jìn)行排序,比如在游戲里面需要對(duì)戰(zhàn)斗力進(jìn)行排行,在組隊(duì)活動(dòng)中需要對(duì)各個(gè)隊(duì)伍的貢獻(xiàn)值進(jìn)行排行,在微信中需要對(duì)各個(gè)好友的步數(shù)進(jìn)行排行,此時(shí)一般會(huì)選擇redis的有序集合對(duì)用戶(hù)的分?jǐn)?shù)進(jìn)行存儲(chǔ),從而實(shí)現(xiàn)排行榜的需求,但是不同的場(chǎng)景排行榜的方式也略有不同,以下根據(jù)自己日常的開(kāi)發(fā)進(jìn)行了一下歸納總結(jié)。

需求:對(duì)組隊(duì)活動(dòng)中各個(gè)隊(duì)伍的貢獻(xiàn)值進(jìn)行排行。

不考慮積分相同

Redis的Sorted Set是String類(lèi)型的有序集合。集合成員是唯一的,這就意味著集合中不能出現(xiàn)重復(fù)的數(shù)據(jù)。

每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類(lèi)型的分?jǐn)?shù)。redis正是通過(guò)分?jǐn)?shù)來(lái)為集合中的成員進(jìn)行從小到大的排序。

有序集合的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。

下面先不考慮積分相同的情況,實(shí)現(xiàn)排行榜:

// 準(zhǔn)備數(shù)據(jù),其中value為每個(gè)隊(duì)伍的ID,score為隊(duì)伍的貢獻(xiàn)值
> zadd z1 5 a 6 b 1 c 2 d 10 e
(integer) 5

// 分頁(yè)查詢(xún)排行榜所有的隊(duì)伍和貢獻(xiàn)值,要使用zrevrange,而不是zrange,貢獻(xiàn)值越大越排在前面
> zrevrange z1 0 2 withscores
1) "e"
2) "10"
3) "b"
4) "6"
5) "a"
6) "5"

// 增加某個(gè)隊(duì)伍的貢獻(xiàn)值
> zincrby z1 3 d
"5"
> zincrby z1 4 c
"5"

// 查詢(xún)排行榜所有的隊(duì)伍
> zrevrange z1 0 -1 withscores
 1) "e"
 2) "10"
 3) "b"
 4) "6"
 5) "d"
 6) "5"
 7) "c"
 8) "5"
 9) "a"
10) "5"

// 查詢(xún)某個(gè)隊(duì)伍的排名
> zrevrank z1 d
(integer) 2

Redis默認(rèn)實(shí)現(xiàn)是相同分?jǐn)?shù)的成員按字典順序排序(09,AZ,a~z),上面使用的是zrevrange,所以是倒序,所以相同分?jǐn)?shù)排序就不能根據(jù)時(shí)間優(yōu)先來(lái)排序。

積分相同按時(shí)間排序,排名唯一

在上面的實(shí)現(xiàn)中,如果兩個(gè)隊(duì)伍的貢獻(xiàn)值相同,也就是積分值相同,無(wú)法根據(jù)時(shí)間的先后進(jìn)行排行。

所以需要設(shè)計(jì)一個(gè)分?jǐn)?shù) = 貢獻(xiàn)值 + 時(shí)間戳 ,誰(shuí)分?jǐn)?shù)大誰(shuí)排前面,最后還要能根據(jù)分?jǐn)?shù)能解析出來(lái)貢獻(xiàn)值。

設(shè)計(jì)1

使用整型存儲(chǔ)分?jǐn)?shù)值,redis中score本身是一個(gè)double類(lèi)型,能精確存儲(chǔ)的最大整型數(shù)字為2^53=9007199254740992(16位)。而精確到毫秒的時(shí)間戳需要13位,此時(shí)留給存儲(chǔ)貢獻(xiàn)值只有3位數(shù)了,當(dāng)前如果時(shí)間只要精確到秒,只需要10位,這樣留給貢獻(xiàn)值就有6位。

整體設(shè)計(jì):高3位表示貢獻(xiàn)值,低13位表示時(shí)間戳。

如果我們簡(jiǎn)單地把score結(jié)構(gòu)由:貢獻(xiàn)值 * 10^13 + 時(shí)間戳 拼湊,因?yàn)榉謹(jǐn)?shù)越大越靠前,而時(shí)間戳越小則越靠前,這樣兩部分的判斷規(guī)則是相反的,無(wú)法簡(jiǎn)單把兩者合成一起成為score。

但是我們可以逆向思維,可以用同一個(gè)足夠大的數(shù)Integer.MAX減去時(shí)間戳,時(shí)間戳越小,則得到的差值越大,這樣我們就可以把score的結(jié)構(gòu)改為:貢獻(xiàn)值 * 10^13 + (Integer.MAX-時(shí)間戳),這樣就能滿(mǎn)足我們的需求了。

設(shè)計(jì)2

由于redis的score值是double類(lèi)型,可以使用整數(shù)部分存儲(chǔ)貢獻(xiàn)值,小數(shù)部分存儲(chǔ)時(shí)間戳,同樣時(shí)間戳的部分使用一個(gè)最大值減去它。

這樣,整體設(shè)計(jì)變?yōu)椋?code>分?jǐn)?shù)=貢獻(xiàn)值 + (Integer.MAX-時(shí)間戳) * 10^-13

弊端:由于分?jǐn)?shù)值是由兩個(gè)變量來(lái)計(jì)算得出,所以在給隊(duì)伍增加貢獻(xiàn)值時(shí),無(wú)法簡(jiǎn)單的使用之前的zincrby來(lái)改變score的值了,這樣在并發(fā)情況下為隊(duì)伍增加貢獻(xiàn)值就會(huì)導(dǎo)致score值不準(zhǔn)確。

錯(cuò)誤情況模擬:

假設(shè)現(xiàn)在隊(duì)伍A的貢獻(xiàn)值為10隊(duì)伍A中的隊(duì)員X為隊(duì)伍增加貢獻(xiàn)值1,在程序中算出score為11.xxx隊(duì)伍A中的隊(duì)員Y為隊(duì)伍增加貢獻(xiàn)值1,在程序中算出score為11.yyy隊(duì)伍A中的隊(duì)員X調(diào)用redis的zadd命令設(shè)置隊(duì)伍的貢獻(xiàn)值為11.xxx隊(duì)伍A中的隊(duì)員Y調(diào)用redis的zadd命令設(shè)置隊(duì)伍的貢獻(xiàn)值為11.yyy最后算出隊(duì)伍A的貢獻(xiàn)值為11,無(wú)法保證增加貢獻(xiàn)值這一個(gè)操作的原子性。

此時(shí)需要借助lua腳本來(lái)保證計(jì)算和設(shè)置貢獻(xiàn)值這兩個(gè)操作的原子性:

// 其中KEYS[1]為排行榜key,KEYS[2]為隊(duì)伍ID
// 其中ARGV[1]為增加的貢獻(xiàn)值,ARGV[2]為Integer.MAX-時(shí)間戳
local score = redis.call('zscore', KEYS[1], KEYS[2]) 
if not(score) then
	score=0 
end 
score=math.floor(score) + tonumber(ARGV[1]) + tonumber(ARGV[2]) 
redis.call('zadd', KEYS[1], score, KEYS[2]) return 1

由于redis中無(wú)法使用時(shí)間函數(shù),所以(Integer.MAX-時(shí)間戳) * 10^-13部分由腳本外程序計(jì)算好傳入。

分頁(yè)查詢(xún)排行榜,查詢(xún)隊(duì)伍的排名等功能都可以繼續(xù)使用上面的命令。

積分相同按時(shí)間排序,并列排名

所謂并列排行榜,就是存在相同排名情況的排行榜。

我們期望的結(jié)果如下表:

隊(duì)伍ID貢獻(xiàn)值排名
a1001
b992
c992
d884
e875

當(dāng)然現(xiàn)實(shí)中也有排名不跳過(guò)的情況,我這里考慮的是排名跳過(guò)的情況。

redis中score的設(shè)計(jì)還是采用上面的分?jǐn)?shù)=貢獻(xiàn)值 + (Integer.MAX-時(shí)間戳) * 10^-13,只是在查詢(xún)排名時(shí)需要進(jìn)行計(jì)算。

比如要查上表中隊(duì)伍b的排名,思路如下:

  • 首先查到隊(duì)伍b的score
  • 再查到跟隊(duì)伍b的score的整數(shù)部分相同(也就是貢獻(xiàn)值一樣),排在第一個(gè)的隊(duì)伍的value(隊(duì)伍ID)
  • 根據(jù)上一步得到的隊(duì)伍ID查詢(xún)此隊(duì)伍的排名就是隊(duì)伍b的排名

使用命令實(shí)現(xiàn)上面的步驟如下:

> zscore 排行榜key teamId
> zrevrangebyscore(排行榜key, 上一步得到的score+1, 上一步得到的score, limit, 0 , 1)
> zrevrank(排行榜key, 上一步得到的teamId)

為了性能考慮,可以使用下面的腳本一次查出來(lái):

// KEYS[1]表示排行榜key
// KEYS[2]表示要查詢(xún)的隊(duì)伍的ID
local rank = 0 
local score = redis.call('zscore', KEYS[1], KEYS[2]) 
if not(score) then
    score=0 
else 
    score=math.floor(score) 
    local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) 
    rank=redis.call('zrevrank', KEYS[1], firstScore[1]) 
end 
return {score,rank}

下面附上分頁(yè)查詢(xún)排行榜的腳本,假如一頁(yè)10條,不用下面的腳本需要查詢(xún)10次上面的腳本,如果連上面的腳本都沒(méi)有使用的話就要查詢(xún)30次redis。

// 排行榜key
// ARGV[1]分頁(yè)起始偏移
// ARGV[2]分頁(yè)結(jié)束偏移
local list = redis.call('zrevrange', KEYS[1], ARGV[1], ARGV[2], 'withscores') 
local result={} 
local i = 1 
for k,v in pairs(list) do 
    if k%2 == 0 then 
        local teamId = list[k-1] 
        local score = math.floor(v) 
        local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) 
        local rank=redis.call('zrevrank', KEYS[1], firstScore[1]) 
        local l = {teamId=teamId, contributionValue=score, teamRank=rank+1} 
        result[i] = l i = i + 1 
    end 
end 
return cjson.encode(result)

此腳本使用了cjson庫(kù),返回的是一個(gè)json。

到此這篇關(guān)于Redis實(shí)現(xiàn)排行榜及相同積分按時(shí)間排序的文章就介紹到這了,更多相關(guān)Redis排行榜按時(shí)間排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis 過(guò)期鍵刪除策略的實(shí)現(xiàn)示例

    Redis 過(guò)期鍵刪除策略的實(shí)現(xiàn)示例

    Redis的過(guò)期數(shù)據(jù)刪除策略主要有三種,包括定時(shí)刪除、惰性刪除和定期刪除,本文主要介紹了Redis 過(guò)期鍵刪除策略的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • redis刪除key下所有value步驟詳解

    redis刪除key下所有value步驟詳解

    在使用Redis時(shí),經(jīng)常需要?jiǎng)h除某個(gè)key下的所有value,本文就來(lái)詳細(xì)的介紹一下redis刪除key下所有value步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Redis數(shù)據(jù)庫(kù)的使用場(chǎng)景介紹(避免誤用Redis)

    Redis數(shù)據(jù)庫(kù)的使用場(chǎng)景介紹(避免誤用Redis)

    這篇文章主要介紹了Redis數(shù)據(jù)庫(kù)的使用場(chǎng)景介紹(避免誤用Redis),本文用簡(jiǎn)要的語(yǔ)言總結(jié)了Redis數(shù)據(jù)庫(kù)的適應(yīng)場(chǎng)合,人而避免錯(cuò)誤的使用它而產(chǎn)生昂貴的維護(hù)代價(jià),需要的朋友可以參考下
    2015-03-03
  • Redis性能大幅提升之Batch批量讀寫(xiě)詳解

    Redis性能大幅提升之Batch批量讀寫(xiě)詳解

    這篇文章主要給大家介紹了關(guān)于Redis性能大幅提升之Batch批量讀寫(xiě)的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • Java實(shí)現(xiàn)多級(jí)緩存的方法詳解

    Java實(shí)現(xiàn)多級(jí)緩存的方法詳解

    對(duì)于高并發(fā)系統(tǒng)來(lái)說(shuō),有三個(gè)重要的機(jī)制來(lái)保障其高效運(yùn)行,它們分別是:緩存、限流和熔斷,所以本文就來(lái)和大家探討一下多級(jí)緩存的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助
    2024-02-02
  • 基于Redis實(shí)現(xiàn)延時(shí)隊(duì)列的優(yōu)化方案小結(jié)

    基于Redis實(shí)現(xiàn)延時(shí)隊(duì)列的優(yōu)化方案小結(jié)

    本文主要介紹了基于Redis實(shí)現(xiàn)延時(shí)隊(duì)列的優(yōu)化方案小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Redis的共享session應(yīng)用實(shí)現(xiàn)短信登錄

    Redis的共享session應(yīng)用實(shí)現(xiàn)短信登錄

    本文主要介紹了Redis的共享session應(yīng)用實(shí)現(xiàn)短信登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷

    詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷

    這篇文章主要為大家詳細(xì)介紹了Redis優(yōu)雅地實(shí)現(xiàn)接口防刷的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Redis分布式鎖如何自動(dòng)續(xù)期的實(shí)現(xiàn)

    Redis分布式鎖如何自動(dòng)續(xù)期的實(shí)現(xiàn)

    本文主要介紹了Redis分布式鎖如何自動(dòng)續(xù)期的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Redis和MySQL保證雙寫(xiě)一致性的問(wèn)題解析

    Redis和MySQL保證雙寫(xiě)一致性的問(wèn)題解析

    Redis和MySQL的雙寫(xiě)一致性指的是在同時(shí)使用緩存和數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)的時(shí)候,保證Redis和MySQL中數(shù)據(jù)的一致性,那么如何才能保證他們的一致性呢,下面小編就來(lái)為大家詳細(xì)講講
    2023-11-11

最新評(píng)論