0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

使用Redis的有序集合實(shí)現(xiàn)排行榜功能和Spring Boot集成

馬哥Linux運(yùn)維 ? 來源:csdn ? 2023-11-20 10:15 ? 次閱讀

Redis 的有序集合(Sorted Set)是一個(gè)基于分?jǐn)?shù)(score)排序的數(shù)據(jù)結(jié)構(gòu),它在 Redis 中非常重要,常用于實(shí)現(xiàn)排行榜、近似計(jì)數(shù)器等功能。

Redis 的有序集合(Sorted Set)是基于跳躍表(Skip List)實(shí)現(xiàn)的。跳躍表是一種高效的數(shù)據(jù)結(jié)構(gòu),其插入、刪除和查找操作的平均時(shí)間復(fù)雜度都是 O(log n),相對于平衡樹(如紅黑樹)的實(shí)現(xiàn)要簡單很多。跳躍表的結(jié)構(gòu)類似于鏈表,每個(gè)節(jié)點(diǎn)除了保存元素值外,還包含一個(gè)指針數(shù)組,分別指向?qū)?yīng)層次的下一個(gè)節(jié)點(diǎn)。這種多級指針的設(shè)計(jì),使得跳表可以跨越多個(gè)節(jié)點(diǎn)進(jìn)行快速搜索,同時(shí)保證跳表結(jié)構(gòu)的高效性和簡潔性。

有序集合的底層數(shù)據(jù)結(jié)構(gòu)由哈希(Hash)和跳躍表組成。在哈希中,存儲了元素及其關(guān)聯(lián)的評分(分?jǐn)?shù))。每個(gè)元素都有一個(gè)唯一的評分,用于確定其在跳躍表中的位置。當(dāng)需要對有序集合進(jìn)行操作時(shí),Redis 首先通過哈希表找到元素及其評分,然后通過跳躍表進(jìn)行相應(yīng)的操作。

以下是 Redis 有序集合(Sorted Set)的一些核心操作及其對應(yīng)的核心代碼分析:

添加元素(ZADD):

有序集合中的元素添加操作是通過哈希表和跳躍表協(xié)同完成的。首先,Redis 將元素值和評分存儲在哈希表中。然后,根據(jù)評分在跳躍表中找到對應(yīng)的位置,并將新元素插入到該位置。

獲取元素(ZRANGE、ZREVRANGE):

有序集合中的獲取元素操作主要依賴于跳躍表。ZRANGE 操作從跳躍表的頭部開始,按照給定的評分范圍返回符合條件的元素。ZREVRANGE 操作則從跳躍表的尾部開始,按照給定的評分范圍返回符合條件的元素。

刪除元素(ZREM):

刪除元素操作首先通過哈希表找到對應(yīng)元素,然后在跳躍表中刪除該元素。Redis 只需要?jiǎng)h除哈希表中的指向該元素的指針,跳躍表中的元素會自動上移。

更新元素評分(ZINCRBY):

更新元素評分操作僅需修改哈希表中對應(yīng)元素的評分,然后重新計(jì)算跳躍表中元素的位置。

獲取有序集合長度(ZCARD):

有序集合長度的操作直接查詢哈希表中的鍵值對數(shù)量。

隨機(jī)獲取元素(ZRANDMEMBER):

隨機(jī)獲取元素操作首先從哈希表中隨機(jī)選擇一個(gè)元素,然后在該元素所在的跳躍表區(qū)間內(nèi)隨機(jī)選擇一個(gè)元素。

通過以上操作,Redis 實(shí)現(xiàn)了高效有序集合(Sorted Set)的數(shù)據(jù)結(jié)構(gòu),提供了高性能的排序和范圍查找功能。

2、實(shí)戰(zhàn)

要使用 Spring Boot 和 Redis 實(shí)現(xiàn)排行榜功能,你可以遵循以下步驟:

引入依賴

在你的 Spring Boot 項(xiàng)目的 pom.xml 文件中,添加以下依賴:

  
      
        org.springframework.boot  
        spring-boot-starter-data-redis  
      
 

配置 Redis

在 application.properties 或 application.yml 文件中配置 Redis 連接信息

# application.properties  
spring.redis.host=localhost  
spring.redis.port=6379  
# application.yml  
spring:  
  redis:  
    host: localhost  
    port: 6379  

創(chuàng)建 Redis 模板

創(chuàng)建一個(gè) RedisTemplate Bean:

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.data.redis.connection.RedisConnectionFactory;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;  
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration  
public class RedisConfig {
    @Bean  
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {  
        RedisTemplate redisTemplate = new RedisTemplate<>();  
        redisTemplate.setConnectionFactory(redisConnectionFactory);  
        redisTemplate.setKeySerializer(new StringRedisSerializer());  
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());  
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());  
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());  
        return redisTemplate;  
    }  
}

創(chuàng)建排行榜實(shí)體類

創(chuàng)建一個(gè)排行榜實(shí)體類,包含用戶 ID、分?jǐn)?shù)等信息:

import java.io.Serializable;
public class RankingEntity implements Serializable {
    private String userId;  
    private double score;
    // 構(gòu)造方法、getter 和 setter 

實(shí)現(xiàn) Redis 排行榜操作

創(chuàng)建一個(gè)服務(wù)類,實(shí)現(xiàn)排行榜的相關(guān)操作,如添加分?jǐn)?shù)、查詢排名等:

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.core.ValueOperations;  
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service  
public class RankingService {
    @Autowired  
    private RedisTemplate redisTemplate;
    private static final String RANKING_KEY = "ranking_list";
    /**  
     * 添加分?jǐn)?shù)  
     * @param userId 用戶 ID  
     * @param score 分?jǐn)?shù)  
     */  
    public void addScore(String userId, double score) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);  
    }
    /**  
     * 查詢排名  
     * @return 排名列表  
     */  
    public List getRankingList() {  
        List rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  
        return rankingList;  
    }
    /**  
     * 查詢用戶排名  
     * @param userId 用戶 ID  
     * @return 用戶排名  
     */  
    public Object getUserRanking(String userId) {  
        return redisTemplate.opsForValue().get(RANKING_KEY + ":" + userId);  
    }
     * @param userId 用戶 ID  
     */  
    public void deleteUserScore(String userId) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        valueOperations.delete(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 更新用戶分?jǐn)?shù)  
     * @param userId 用戶 ID  
     * @param score 新的分?jǐn)?shù)  
     */  
    public void updateUserScore(String userId, double score) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);  
    }
    /**  
     * 獲取用戶排名列表的長度  
     * @return 用戶排名列表的長度  
     */  
    public long getUserRankingListSize() {  
        return redisTemplate.opsForList().size(RANKING_KEY);  
    }
    /**  
     * 在用戶排名列表中插入用戶分?jǐn)?shù)  
     * @param userId 用戶 ID  
     * @param score 分?jǐn)?shù)  
     * @param index 插入位置,0 表示插入到列表頭部,負(fù)數(shù)表示插入到列表尾部  
     */  
    public void insertUserScore(String userId, double score, long index) {  
        List rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  
        redisTemplate.opsForList().leftPush(RANKING_KEY, score, index);  
    }
    /**  
     * 在用戶排名列表中刪除用戶分?jǐn)?shù)  
     * @param userId 用戶 ID  
     * @param index 刪除位置,0 表示刪除第一個(gè)元素,1 表示刪除第二個(gè)元素,依此類推  
     */  
    public void deleteUserScore(String userId, long index) {  
        List rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);  
    }
    /**  
     * 獲取用戶排名列表中的最后一個(gè)元素  
     * @return 用戶排名列表中的最后一個(gè)元素  
     */  
    public Object getLastUserScore() {  
        return redisTemplate.opsForList().rightPop(RANKING_KEY);  
    }
    /**  
     * 獲取用戶排名列表中的第一個(gè)元素  
     * @return 用戶排名列表中的第一個(gè)元素  
     */  
    public Object getFirstUserScore() {  
        return redisTemplate.opsForList().leftPop(RANKING_KEY);  
    }
    /**  
     * 在用戶排名列表中添加元素  
     * @param score 添加的分?jǐn)?shù)  
     */  
    public void addUserScore(double score) {  
        redisTemplate.opsForList().rightPush(RANKING_KEY, score);  
    }
    /**  
     * 在用戶排名列表中刪除元素  
     * @param index 刪除位置,0 表示刪除第一個(gè)元素,1 表示刪除第二個(gè)元素,依此類推  
     */  
    public void deleteUserScore(long index) {  
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);  
    }
    /**  
     * 獲取用戶排名列表  
     * @return 用戶排名列表  
     */  
    public List getUserRankingList() {  
        return redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  
    }
    /**  
     * 設(shè)置用戶排名列表的長度  
     * @param length 用戶排名列表的新長度  
     */  
    public void setUserRankingListLength(long length) {  
        redisTemplate.opsForList().setSize(RANKING_KEY, length);  
    }
    /**  
     * 獲取用戶排名  
     *  
     * @param userId 用戶 ID  
     * @return 用戶排名,如果用戶不存在,返回 -1  
     */  
    public int getUserRanking(String userId) {  
        List rankingList = getRankingList();  
        Object userScore = getUserRanking(userId);  
        if (userScore == null) {  
            return -1;  
        }
                     int rank = 0;    
        for (Object score : rankingList) {    
            if (score.equals(userScore)) {    
                break;    
            }    
            rank++;    
        }    
        return rank;    
    }
    /**  
     * 獲取用戶排名列表中的指定位置的元素  
     *  
     * @param index 指定位置,從 0 開始  
     * @return 用戶排名列表中的指定位置的元素  
     */  
    public Object getUserRankingListElement(long index) {  
        return redisTemplate.opsForList().range(RANKING_KEY, 0, -1).get(index);  
    }
    /**  
     * 獲取用戶排名列表中的用戶分?jǐn)?shù)  
     *  
     * @param userId 用戶 ID  
     * @return 用戶排名列表中的用戶分?jǐn)?shù),如果用戶不存在,返回 null  
     */  
    public Object getUserRanking(String userId) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        return valueOperations.get(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 是否存在用戶  
     *  
     * @param userId 用戶 ID  
     * @return 是否存在用戶  
     */  
    public boolean existsUser(String userId) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        return valueOperations.hasKey(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 清除所有用戶排名數(shù)據(jù)  
     */  
    public void clearAllUserRankingData() {  
        redisTemplate.delete(RANKING_KEY);  
    }  
}







審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2253

    瀏覽量

    94278
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    370

    瀏覽量

    10830

原文標(biāo)題:Redis實(shí)戰(zhàn) | 使用Redis 的有序集合(Sorted Set)實(shí)現(xiàn)排行榜功能,和Spring Boot集成

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    HarmonyOS開發(fā)案例:【排行榜頁面】

    本課程使用聲明式語法和組件化基礎(chǔ)知識,搭建一個(gè)可刷新的排行榜頁面。在排行榜頁面中,使用循環(huán)渲染控制語法來實(shí)現(xiàn)列表數(shù)據(jù)渲染,使用@Builder創(chuàng)建排行列表布局內(nèi)容,使用裝飾器@Stat
    的頭像 發(fā)表于 04-30 16:16 ?1865次閱讀
    HarmonyOS開發(fā)案例:【<b class='flag-5'>排行榜</b>頁面】

    中國IC設(shè)計(jì)公司排行榜

    作者:林曉林  中國IC設(shè)計(jì)公司排行榜:近日,市場調(diào)研公司iSuppli出臺了2005年度中國IC設(shè)計(jì)公司排行榜,與中國半導(dǎo)體協(xié)會的排名不同,此次名列榜首的是來自香港的晶門
    發(fā)表于 05-26 14:29

    資料下載總排行榜

    資料下載總排行榜,怎么就那幾個(gè)啊,怎么下載到人氣高的資料?資料茫茫,我相信大家的眼睛雪亮的。求方法收集些好的資料。。。
    發(fā)表于 03-05 16:24

    各種排行榜匯總貼!?。。?!

    本帖最后由 dongyumin 于 2013-7-31 11:39 編輯 1.2012網(wǎng)上各地年終獎(jiǎng)排行榜,科技、電子企業(yè)全面領(lǐng)跑!https://bbs.elecfans.com
    發(fā)表于 07-30 11:55

    2014年10月 TIOBE 編程語言排行榜發(fā)布

    2014年10月的 TIOBE 編程語言排行榜發(fā)布了,該版本最大的兩點(diǎn)是 Google 的 Dart 語言首次進(jìn)入前 20 名。其競爭者包括 CoffeeScript 目前排名 133,TypeScript 排名 122.想知道完整的排名表格請回復(fù)
    發(fā)表于 12-08 13:46

    小米放出“手機(jī)電量排行榜” 為續(xù)航神機(jī)Max 2造勢

    小米手機(jī)家族的電量排行榜,并向網(wǎng)友征詢:“你覺得小米Max2多大電量夠你用? ”從排行榜來看,現(xiàn)款小米Max以4850mAh的容量排名第一,其次是小米MIX(4400mAh)、紅米4(4100mAh
    發(fā)表于 06-03 14:20

    Redis有序集合詳細(xì)步驟

    利用Redis Sorted Set實(shí)現(xiàn)排行榜功能
    發(fā)表于 05-21 14:09

    MapReduce框架音樂排行榜案例

    Hadoop綜合實(shí)戰(zhàn)之MapReduce運(yùn)算優(yōu)化——音樂排行榜
    發(fā)表于 10-16 12:20

    Spring bootRedis的使用

    【本人禿頂程序員】springboot專輯:Spring bootRedis的使用
    發(fā)表于 03-27 11:42

    求職必知獨(dú)角獸公司排行榜

    世界第 3 的滴滴裁員,求職必知獨(dú)角獸公司排行榜
    發(fā)表于 06-18 07:30

    2019年2月編程語言排行榜分享

    2019年2月編程語言排行榜
    發(fā)表于 07-14 10:28

    2020年最新主板型號排行榜 精選資料推薦

    2020年最新主板型號排行榜2020主板型號天梯圖2020主板選購指南一、Intel、AMD電腦主板的辨別二、主板芯片組級別三、板形四、主板對電腦性能有什么影響在使用電腦的時(shí)候,我們有時(shí)候會需要更換
    發(fā)表于 07-26 06:16

    玩轉(zhuǎn)Redis-使用有序集合(sorted sets)實(shí)現(xiàn)投票游戲

    中所有參與的人的排行榜。* zinterstore 是求兩個(gè)集合的交集,通過它,可以獲得同時(shí)參加多個(gè)候選人投票的名單列表。* zrevrank 方便的查詢某個(gè)元素在有序集合中的位置,也
    的頭像 發(fā)表于 09-26 12:40 ?2558次閱讀

    基于排行榜功能使用redis中的有序集合實(shí)現(xiàn)

    排行榜功能是一個(gè)很普遍的需求,對于這類需求如果數(shù)據(jù)總量過大用mysql實(shí)現(xiàn)的話會很浪費(fèi)性能。
    的頭像 發(fā)表于 10-21 09:59 ?975次閱讀

    Redis的常用場景有哪些

    策略,所以,現(xiàn)在Redis用在緩存的場合非常多。 2、排行榜 很多網(wǎng)站都有排行榜應(yīng)用的,如京東的月度銷量榜單、商品按時(shí)間的上新排行榜等。Redis
    的頭像 發(fā)表于 10-09 10:44 ?630次閱讀