最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Redis的ZSET實現(xiàn)用戶邀請排行榜

 更新時間:2025年11月26日 09:37:50   作者:埃澤漫筆  
本文主要介紹了在項目中實現(xiàn)用戶邀請功能并基于積分進行排名的解決方案,使用了Redis的ZSET數(shù)據(jù)結(jié)構(gòu)和Redisson的RLock進行加鎖以避免并發(fā)問題,感興趣的可以了解一下

背景

在我們的項目中,有用戶的邀請功能,每一次邀請別人注冊,會有一定的積分,然后我們同時提供了一個排行榜的功能,可以基于這個積分進行排名。

排名的功能比較簡單,就是基于積分去排序就行了,這里面我們利用了Redis的ZSET的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)快速的排序。

因為ZSET是一個天然有序的數(shù)據(jù)結(jié)構(gòu),我們可以把積分當(dāng)做score,用戶id當(dāng)做member,放到zset中,zset會默認按照SCORE進行排序的。

偽代碼實現(xiàn)

以下是用戶接受邀請部分的代碼實現(xiàn):

@DistributeLock(keyExpression = "#telephone", scene = "USER_REGISTER")
public UserOperatorResponse register(String telephone, String inviteCode) {
	//用戶名生成
 
    String inviterId = null;
    if (StringUtils.isNotBlank(inviteCode)) {
        User inviter = userMapper.findByInviteCode(inviteCode);
        if (inviter != null) {
            inviterId = inviter.getId().toString();
        }
    }
 
    //用戶注冊
 
    //更新排名
    updateInviteRank(inviterId);
    
 
	//其他邏輯
}

updateInviteRank的額代碼邏輯如下:

private void updateInviteRank(String inviterId) {
    // 如果邀請者ID為空,則直接返回,不進行操作
    if (inviterId == null) {
        return;
    }
 
    // 獲取Redisson的鎖對象
    RLock rLock = redissonClient.getLock(inviterId);
    // 對邀請者ID對應(yīng)的鎖進行加鎖操作,避免并發(fā)更新
    rLock.lock();
    try {
        // 獲取邀請者的當(dāng)前排名分數(shù)
        Double score = inviteRank.getScore(inviterId);
        // 如果當(dāng)前分數(shù)為空,則設(shè)置默認為0.0
        if (score == null) {
            score = 0.0;
        }
        // 將邀請者的排名分數(shù)增加100.0,并更新到排行榜中
        inviteRank.add(score + 100.0, inviterId);
    } finally {
        // 最終釋放邀請者ID對應(yīng)的鎖
        rLock.unlock();
    }
}
 

這里主要是用到了Redisson的RLock進行了加鎖,并且是用的lock方法,在加鎖失敗時阻塞一直嘗試。主要就是避免多個用戶同時被邀請時,更新分數(shù)會出現(xiàn)并發(fā)而導(dǎo)致分數(shù)累加錯誤。

這里面的排行榜inviteRank,其實是:

private RScoredSortedSet<String> inviteRank;
 
    @Override
    public void afterPropertiesSet() throws Exception {
        
        this.inviteRank = redissonClient.getScoredSortedSet("inviteRank");
    }

在以上邏輯中進行初始化和實例化的,其實他是一個RScoredSortedSet,是一個支持排序的Set,他提供了很多方法可以方便的實現(xiàn)排名的功能,如:

  • getScore:獲取指定成員的分數(shù)。
  • add:向有序集合中添加一個成員,指定該成員的分數(shù)。
  • rank:獲取指定成員在有序集合中的排名(從小到大排序,排名從 0 開始)。
  • revRank:獲取指定成員在有序集合中的排名(從大到小排序,排名從 0 開始)。
  • entryRange:獲取分數(shù)在指定范圍內(nèi)的成員及其分數(shù)的集合。

比如我們提供了以下幾個和排名有關(guān)的方法,其實就是對上述方法的一些封裝:

//獲取指定用戶的排名,按照分數(shù)從高到低
public Integer getInviteRank(String userId) {
    Integer rank = inviteRank.revRank(userId);
    if (rank != null) {
        return rank + 1;
    }
    return null;
}
//按照分數(shù)從高到低,獲取前N個用戶的排名信息
public List<InviteRankInfo> getTopN(Integer topN) {
    Collection<ScoredEntry<String>> rankInfos = inviteRank.entryRangeReversed(0, topN - 1);
    List<InviteRankInfo> inviteRankInfos = new ArrayList<>();
 
    if (rankInfos != null) {
        for (ScoredEntry<String> rankInfo : rankInfos) {
            InviteRankInfo inviteRankInfo = new InviteRankInfo();
            String userId = rankInfo.getValue();
            if (StringUtils.isNotBlank(userId)) {
                User user = findById(Long.valueOf(userId));
                if (user != null) {
                    inviteRankInfo.setNickName(user.getNickName());
                    inviteRankInfo.setInviteCode(user.getInviteCode());
                    inviteRankInfo.setInviteCount(rankInfo.getScore().intValue() / 100);
                    inviteRankInfos.add(inviteRankInfo);
                }
            }
        }
    }
 
    return inviteRankInfos;
}

多維度排行榜實現(xiàn)

前面的實現(xiàn)中,如果分數(shù)相同,那么排序的結(jié)果是不確定的,那么如果我們想要實現(xiàn)多維度排名,即先按照分數(shù)排,分數(shù)相同的話按照上榜時間排,如何實現(xiàn)呢?

為了實現(xiàn)分數(shù)相同按照時間順序排序,我們可以將分數(shù)score設(shè)置為一個浮點數(shù),其中整數(shù)部分為得分,小數(shù)部分為時間戳,如下所示:

score = 分數(shù) + 時間戳/1e13

假設(shè)現(xiàn)在的時間戳是1680417299000,除以1e13得到0.1680417299000,再加上一個固定的分數(shù)(比如10),那么最終的分數(shù)就是10.1680417299000,可以將它作為zset中某個成員的分數(shù),用來排序。

這么做了之后,假如有四個數(shù)字:

10.1680417299000、10.1680417299011、11.1680417299000、11.1680417299011

他們按照倒序拍完順序之后,會是:

11.1680417299011>11.1680417299000>10.1680417299011>10.1680417299000

實現(xiàn)了分數(shù)倒序排列,分數(shù)相同時間戳大(上榜更晚的)的排在了前面,這和我們的需求相反了,所以,就需要在做一次轉(zhuǎn)換。

score = 分數(shù) + 1-時間戳/1e13

因為時間戳是這種形式1708746590000 ,共有13位,而1e13是10000000000000,即1后面13個0,所以用時間戳/1e13就能得到一個小數(shù)

這樣可以保證分數(shù)相同時,按照時間戳從小到大排序,即先得分的先被排在前面。

修改后的代碼如下:

private void updateInviteRank(String inviterId) {
    if (inviterId == null) {
        return;
    }
    //1、這里因為是一個私有方法,無法通過注解方式實現(xiàn)分布式鎖。
    //2、register方法已經(jīng)加了鎖,這里需要二次加鎖的原因是register鎖的是注冊人,這里鎖的是邀請人
    RLock rLock = redissonClient.getLock(inviterId);
    rLock.lock();
    try {
        //獲取當(dāng)前用戶的積分
        Double score = inviteRank.getScore(inviterId);
        if (score == null) {
            score = 0.0;
        }
 
        //獲取最近一次上榜時間
        long currentTimeStamp = System.currentTimeMillis();
        //把上榜時間轉(zhuǎn)成小數(shù)(時間戳13位,所以除以10000000000000能轉(zhuǎn)成小數(shù)),并且倒序排列(用1減),即上榜時間越早,分數(shù)越大(時間越晚,時間戳越大,用1減一下,就反過來了)
        double timePartScore = 1 - (double) currentTimeStamp / 10000000000000L;
 
        //1、當(dāng)前積分保留整數(shù),即移除上一次的小數(shù)位
        //2、當(dāng)前積分加100,表示新邀請了一個用戶
        //3、加上“最近一次上榜時間的倒序小數(shù)位“作為score
        inviteRank.add(score.intValue() + 100.0 + timePartScore, inviterId);
    } finally {
        rLock.unlock();
    }
}

到此這篇關(guān)于基于Redis的ZSET實現(xiàn)用戶邀請排行榜的文章就介紹到這了,更多相關(guān)Redis ZSET用戶邀請排行榜內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis集合類型的常用命令小結(jié)

    Redis集合類型的常用命令小結(jié)

    這篇文章給大家整理了在操作Redis集合類型中的常用命令,文章總結(jié)的很全面,對大家學(xué)習(xí)Redis具有一定的參考借鑒價值,下面來一起看看吧。
    2016-09-09
  • MyBatis緩存和二級緩存整合Redis的解決方案

    MyBatis緩存和二級緩存整合Redis的解決方案

    這篇文章主要介紹了MyBatis緩存和二級緩存整合Redis,將MyBatis緩存和二級緩存整合Redis,可以提高查詢效率,同時也能保證數(shù)據(jù)的可靠性和一致性,需要的朋友可以參考下
    2023-07-07
  • redis yml配置的用法小結(jié)

    redis yml配置的用法小結(jié)

    RedisYML配置是Redis的一種配置文件格式,,對Redis的配置進行統(tǒng)一管理,本文就來介紹了redis yml配置的用法小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • 單線程Redis快的4 個原因總結(jié)

    單線程Redis快的4 個原因總結(jié)

    作為內(nèi)存中數(shù)據(jù)存儲,Redis 以其速度和性能著稱,通常被用作大多數(shù)后端服務(wù)的緩存解決方案,但是,在內(nèi)部,Redis 采用單線程架構(gòu),為什么單線程設(shè)計依然會有這么高的性能,在本文中,讓我們深入探討為什么 Redis 才有單線程架構(gòu)
    2023-07-07
  • redis在php中常用的語法【推薦】

    redis在php中常用的語法【推薦】

    string是redis最基本的類型,而且string類型是二進制安全的。這篇文章主要介紹了redis在php中常用的語法,需要的朋友可以參考下
    2018-08-08
  • Redis實現(xiàn)分布式鎖的幾種方法總結(jié)

    Redis實現(xiàn)分布式鎖的幾種方法總結(jié)

    這篇文章主要介紹了Redis實現(xiàn)分布式鎖的幾種方法總結(jié)的相關(guān)資料, Redis實現(xiàn)與Zookeeper實現(xiàn)和數(shù)據(jù)庫實現(xiàn),需要的朋友可以參考下
    2017-07-07
  • Redis6.0搭建集群Redis-cluster的方法

    Redis6.0搭建集群Redis-cluster的方法

    這篇文章主要介紹了Redis6.0搭建集群Redis-cluster的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 解決redis-cli報錯Could not connect to Redis at 127.0.0.1:6379: Connection refused

    解決redis-cli報錯Could not connect to Redis&

    這篇文章主要介紹了解決redis-cli報錯Could not connect to Redis at 127.0.0.1:6379: Connection refused,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Redis搭建一主多從的實現(xiàn)步驟

    Redis搭建一主多從的實現(xiàn)步驟

    本文主要介紹了在Linux環(huán)境下配置Redis的一主兩從架構(gòu),包括創(chuàng)建配置文件,設(shè)置不同端口,啟動服務(wù),以及通過inforeplication命令檢查復(fù)制狀態(tài),感興趣都可以了解一下
    2025-10-10
  • redis快速部署為docker容器的方法實現(xiàn)

    redis快速部署為docker容器的方法實現(xiàn)

    部署 Redis 作為 Docker 容器是一種快速、靈活且可重復(fù)使用的方式,特別適合開發(fā)、測試和部署環(huán)境,本文主要介紹了redis快速部署為docker容器的方法實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-05-05

最新評論

临安市| 巴林左旗| 小金县| 崇仁县| 加查县| 大埔区| 玛纳斯县| 金堂县| 万州区| 贺兰县| 开阳县| 乐陵市| 吴旗县| 都匀市| 庆云县| 鄂温| 桦甸市| 搜索| 麻江县| 柘荣县| 贵定县| 左云县| 浠水县| 合阳县| 嘉定区| 阳春市| 宝坻区| 泗水县| 乃东县| 尼勒克县| 永修县| 乌什县| 永济市| 常熟市| 四川省| 宜宾县| 安西县| 济南市| 方城县| 宿松县| 旌德县|