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

SpringBoot整合Redisson實(shí)現(xiàn)高性能實(shí)時(shí)排行榜

 更新時(shí)間:2025年04月18日 10:11:57   作者:程序員總部  
在當(dāng)今的互聯(lián)網(wǎng)應(yīng)用中,排行榜功能幾乎無處不在,那么如何用Spring Boot和Redisson快速搭建一個(gè)高性能的實(shí)時(shí)排行榜呢,下面我們就來詳細(xì)聊聊這個(gè)技術(shù)方案吧

在當(dāng)今的互聯(lián)網(wǎng)應(yīng)用中,排行榜功能幾乎無處不在!無論是電商平臺(tái)的銷量榜單、游戲中的玩家戰(zhàn)力排行,還是社交媒體的熱度榜單,實(shí)時(shí)、高效的排行榜系統(tǒng)都是提升用戶體驗(yàn)的關(guān)鍵。那么問題來了:如何用Spring Boot和Redisson快速搭建一個(gè)高性能的實(shí)時(shí)排行榜?今天我們就來詳細(xì)聊聊這個(gè)技術(shù)方案!

為什么選擇Redisson

首先,Redisson是一個(gè)基于Redis的Java客戶端,它不僅封裝了Redis的基本操作,還提供了分布式鎖、布隆過濾器、排行榜等高級(jí)功能。相比直接操作Redis,Redisson的API更符合Java開發(fā)者的習(xí)慣,而且性能優(yōu)化得非常好。舉個(gè)例子,它的RLexSortedSet和RScoredSortedSet可以直接用來實(shí)現(xiàn)排行榜,支持毫秒級(jí)的實(shí)時(shí)更新!

// 示例:初始化Redisson客戶端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

// 獲取一個(gè)帶分?jǐn)?shù)的有序集合
RScoredSortedSet<String> ranking = redisson.getScoredSortedSet("user_ranking");

Spring Boot如何整合Redisson

整合過程非常簡(jiǎn)單!只需要幾步:

添加依賴:在pom.xml中加入Redisson的Spring Boot Starter。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.8</version>
</dependency>

配置Redis連接:在application.yml中填寫Redis的地址和密碼。

spring:
  redis:
    host: 127.0.0.1
    port: 6379

注入RedissonClient:直接通過@Autowired就能使用Redisson的功能。

@Autowired
private RedissonClient redissonClient;

是不是很簡(jiǎn)單?但別急,這只是開始!接下來才是真正的核心邏輯。

實(shí)現(xiàn)實(shí)時(shí)排行榜的關(guān)鍵邏輯

排行榜的核心功能通常包括:

  • 更新分?jǐn)?shù):比如用戶完成一筆訂單后增加積分。
  • 獲取排名:查詢某個(gè)用戶的當(dāng)前排名。
  • 獲取Top N:展示前100名的榜單。

用Redisson實(shí)現(xiàn)這些功能非常直觀:

// 更新用戶分?jǐn)?shù)
ranking.addScore("user1", 100);

// 獲取用戶排名(從高到低)
int rank = ranking.rank("user1");

// 獲取Top 10
Collection<String> top10 = ranking.valueRangeReversed(0, 9);

這里有個(gè)小技巧:Redisson的valueRangeReversed方法可以直接返回倒序結(jié)果,避免了手動(dòng)排序的開銷!

性能優(yōu)化與實(shí)戰(zhàn)坑點(diǎn)

在實(shí)際項(xiàng)目中,排行榜可能會(huì)面臨高并發(fā)更新的問題。比如雙十一期間,電商平臺(tái)的銷量榜單每秒鐘要更新成千上萬次!這時(shí)候就需要考慮以下幾點(diǎn):

  • 批量操作:Redisson支持管道(Pipeline)和批量命令,能顯著減少網(wǎng)絡(luò)開銷。
  • 分布式鎖:如果涉及復(fù)雜的計(jì)算(比如積分加權(quán)),可以用RLock避免并發(fā)問題。
  • 內(nèi)存優(yōu)化:Redis的zset默認(rèn)用ziplist存儲(chǔ)少量數(shù)據(jù),但數(shù)據(jù)量大時(shí)會(huì)自動(dòng)轉(zhuǎn)為skiplist,這時(shí)候要注意內(nèi)存占用。
// 使用管道批量更新
RBatch batch = redisson.createBatch();
batch.getScoredSortedSet("ranking").addScoreAsync("user1", 10);
batch.getScoredSortedSet("ranking").addScoreAsync("user2", 20);
batch.execute();

擴(kuò)展:多維度排行榜

有時(shí)候我們需要多維度的排名,比如“周榜”和“總榜”并存。這時(shí)候可以用Redisson的RScoredSortedSet配合不同的Key來實(shí)現(xiàn):

RScoredSortedSet<String> weeklyRanking = redisson.getScoredSortedSet("ranking:weekly");
RScoredSortedSet<String> totalRanking = redisson.getScoredSortedSet("ranking:total");

???????// 每周清零周榜
weeklyRanking.clear();

結(jié)語

通過Spring Boot和Redisson,我們能夠輕松實(shí)現(xiàn)一個(gè)高性能的實(shí)時(shí)排行榜系統(tǒng)。從基礎(chǔ)的功能到性能優(yōu)化,Redisson都提供了簡(jiǎn)潔而強(qiáng)大的API。如果你正在面臨類似的需求,不妨動(dòng)手試試吧!遇到問題也別慌,多查文檔、多交流,技術(shù)成長(zhǎng)就是這么一步步來的。

方法補(bǔ)充

SpringBoot+Redission實(shí)現(xiàn)排行榜功能

1.引入Redis和Redission依賴

<!-- redis -->  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>  
  
<!-- redisson -->  
<dependency>  
    <groupId>org.redisson</groupId>  
    <artifactId>redisson-spring-boot-starter</artifactId>  
    <version>3.20.1</version>  
</dependency>

2.application.yml配置

--- # redis配置  
spring:  
  redis:  
    # 地址  
    host: localhost  
    # 端口,默認(rèn)為6379  
    port: 6379  
    # 數(shù)據(jù)庫索引  
    database: 0  
    # 密碼(如沒有密碼請(qǐng)注釋掉)  
    # password:    # 連接超時(shí)時(shí)間  
    timeout: 10s  
    # 是否開啟ssl  
    ssl: false  
  
--- # redisson配置  
redisson:  
  # redis key前綴  
  keyPrefix: ${spring.application.name}  
  # 線程池?cái)?shù)量  
  threads: 4  
  # Netty線程池?cái)?shù)量  
  nettyThreads: 8  
  # 單節(jié)點(diǎn)配置  
  singleServerConfig:  
    # 客戶端名稱  
    clientName: ${spring.application.name}  
    # 最小空閑連接數(shù)  
    connectionMinimumIdleSize: 8  
    # 連接池大小  
    connectionPoolSize: 32  
    # 連接空閑超時(shí),單位:毫秒  
    idleConnectionTimeout: 10000  
    # 命令等待超時(shí),單位:毫秒  
    timeout: 3000  
    # 發(fā)布和訂閱連接池大小  
    subscriptionConnectionPoolSize: 50

3.Java代碼

Constant

/**  
 * @author Baisu  
 * @classname RankingConstant  
 * @description 排行榜常量數(shù)據(jù)  
 * @since 2024/5/6  
 */
public class RankingConstant {  
  
    public static final Long BASIC_QUANTITY = 10000000000000L;  
  
    public static final Long MAXIMUM_TIME_TIMIT = 29991231235959L;  
}

Controller

import cn.hutool.core.date.DateTime;  
import cn.hutool.core.date.DateUtil;  
import com.ranking.demo.common.R;  
import com.ranking.demo.common.constant.RankingConstant;  
import com.ranking.demo.demain.RankingVo;  
import com.ranking.demo.utils.RankingUtil;  
import com.ranking.demo.utils.RedisKey;  
import com.ranking.demo.utils.RedisUtil;  
import org.redisson.client.protocol.ScoredEntry;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.web.bind.annotation.*;  
  
import java.util.ArrayList;  
import java.util.Collection;  
import java.util.List;  
  
/**  
 * @author Baisu  
 * @since 2024/4/28  
 */
@RestController  
public class DemoRankingController {  
  
    @Value("${spring.application.name}")  
    private String applicationName;  
  
    /**  
     * 項(xiàng)目啟動(dòng)測(cè)試方法  
     *  
     * @return applicationName  
     */    
    @GetMapping("")  
    public String demo() {  
        return applicationName;  
    }  
  
    /**  
     * 生成測(cè)試數(shù)據(jù)  
     *  
     * @return ok  
     */    
    @GetMapping("/generate_test_data")  
    public R<Object> generateTestData() {  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 1L, "10001");  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 2L, "10002");  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 3L, "10003");  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 4L, "10004");  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 5L, "10005");  
        return R.ok();  
    }  
  
    /**  
     * 獲取排行榜數(shù)據(jù)  
     *  
     * @param top 數(shù)量  
     * @return 排行榜數(shù)據(jù)  
     */  
    @GetMapping("/get_ranking")  
    public R<Object> getRanking(@RequestParam("top") Integer top) {  
        Collection<ScoredEntry<Object>> ranking = RedisUtil.getRanking(RedisKey.getRankingDemoKey(), 0, top - 1);  
        if (ranking.size() == 0) {  
            return R.fail("暫無排行榜數(shù)據(jù)");  
        }  
        List<RankingVo> list = new ArrayList<>();  
        for (ScoredEntry<Object> entry : ranking) {  
            RankingVo vo = new RankingVo();  
            vo.setMember(entry.getValue().toString());  
            vo.setScore(RankingUtil.getScore(entry.getScore()));  
            vo.setTime(RankingUtil.getTimeStr(entry.getScore()));  
            list.add(vo);  
        }  
        return R.ok(list);  
    }  
  
    /**  
     * 增加成員分?jǐn)?shù)值  
     *  
     * @param member 成員  
     * @return 是否增加成功  
     */  
    @GetMapping("/add_score_by_member")  
    public R<Object> addScoreByMember(@RequestParam("member") String member) {  
        Double scoreByMember = RedisUtil.getScoreByMember(RedisKey.getRankingDemoKey(), member);  
        if (scoreByMember == null) {  
            scoreByMember = 0.0;  
        }  
        RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), RankingUtil.getScore(scoreByMember) + 1, member);  
        return R.ok();  
    }  
  
    /**  
     * 獲取成員分?jǐn)?shù)值  
     *  
     * @param member 成員  
     * @return 分?jǐn)?shù)值  
     */  
    @GetMapping("/get_score_by_member")  
    public R<Object> getScoreByMember(@RequestParam("member") String member) {  
        Double scoreByMember = RedisUtil.getScoreByMember(RedisKey.getRankingDemoKey(), member);  
        if (scoreByMember == null) {  
            return R.fail("該成員不存在");  
        }  
        RankingVo vo = new RankingVo();  
        vo.setMember(member);  
        vo.setScore(RankingUtil.getScore(scoreByMember));  
        vo.setTime(RankingUtil.getTimeStr(scoreByMember));  
        return R.ok(vo);  
    }  
  
}

Domain

import lombok.Data;  
  
/**  
 * @author Baisu  
 * @classname RankingVo  
 * @description 排行榜展示類  
 * @since 2024/5/6  
 */
@Data  
public class RankingVo {  
  
    /**  
     * 成員  
     */  
    private String member;  
    /**  
     * 分?jǐn)?shù)值  
     */  
    private Long score;  
    /**  
     * 時(shí)間  
     */  
    private String time;  
  
}

Utils

/**  
 * @author Baisu  
 * @classname RedisKey  
 * @description Redis索引  
 * @since 2024/5/6  
 */
public class RedisKey {  
  
    private static final String RANKING_DEMO_KEY = "ranking_demo";  
  
    public static String getRankingDemoKey() {  
        return RANKING_DEMO_KEY;  
    }  
}
import cn.hutool.core.date.DateTime;  
import cn.hutool.core.date.DateUtil;  
import cn.hutool.extra.spring.SpringUtil;  
import com.ranking.demo.common.constant.RankingConstant;  
import org.redisson.api.RScoredSortedSet;  
import org.redisson.api.RedissonClient;  
import org.redisson.client.protocol.ScoredEntry;  
  
import java.util.Collection;  
  
/**  
 * @author Baisu  
 * @classname RedisUtil  
 * @description Redis工具類  
 * @since 2024/5/6  
 */
public class RedisUtil {  
  
    private static final RedissonClient REDISSON_CLIENT = SpringUtil.getBean(RedissonClient.class);  
  
    /**  
     * 向有序集合中添加指定分?jǐn)?shù)的成員  
     *  
     * @param key    有序集索引  
     * @param score  分?jǐn)?shù)  
     * @param member 成員  
     * @return 是否成功  
     */  
    public static boolean addScoreByMember(String key, Long score, String member) {  
        RScoredSortedSet<String> rScoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  
        double v = score * RankingConstant.BASIC_QUANTITY + (RankingConstant.MAXIMUM_TIME_TIMIT - Long.parseLong(DateUtil.format(DateTime.now(), RankingUtil.FORMAT)));  
        return rScoredSortedSet.add(v, member);  
    }  
  
    /**  
     * 返回有序集中成員的分?jǐn)?shù)值  
     *  
     * @param key    有序集索引  
     * @param member 成員  
     * @return 分?jǐn)?shù)值(Double)  
     */    
     public static Double getScoreByMember(String key, String member) {  
        RScoredSortedSet<Object> scoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  
        return scoredSortedSet.getScore(member);  
    }  
  
    /**  
     * 返回有序集中指定位置的成員集合  
     *  
     * @param key   有序集索引  
     * @param start 開始索引  
     * @param end   結(jié)束索引  
     * @return 成員集合  
     */  
    public static Collection<ScoredEntry<Object>> getRanking(String key, int start, int end) {  
        RScoredSortedSet<Object> rScoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  
        return rScoredSortedSet.entryRangeReversed(start, end);  
    }  
  
}
import cn.hutool.core.date.DateUtil;  
import com.ranking.demo.common.constant.RankingConstant;  
  
/**  
 * @author Baisu  
 * @classname RankingUtil  
 * @description 排行榜工具類  
 * @since 2024/5/7  
 */
 public class RankingUtil {  
  
    public static final String FORMAT = "yyyyMMddHHmmss";  
  
    public static Long getScore(Double score) {  
        return Math.round(Math.floor(score / RankingConstant.BASIC_QUANTITY));  
    }  
  
    public static String getTimeStr(Double score) {  
        return String.valueOf(DateUtil.parse(String.valueOf(RankingConstant.MAXIMUM_TIME_TIMIT - Math.round(Math.floor(score)) % RankingConstant.BASIC_QUANTITY)));  
    }  
}

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

相關(guān)文章

  • java多線程實(shí)現(xiàn)取款小程序

    java多線程實(shí)現(xiàn)取款小程序

    這篇文章主要為大家詳細(xì)介紹了java多線程實(shí)現(xiàn)取款小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 解決Eclipse發(fā)布到Tomcat丟失依賴jar包的問題

    解決Eclipse發(fā)布到Tomcat丟失依賴jar包的問題

    這篇文章介紹了如何在Eclipse中配置部署裝配功能,以確保在將Web項(xiàng)目發(fā)布到Tomcat服務(wù)器時(shí)不會(huì)丟失任何依賴jar包,通過手動(dòng)配置或使用構(gòu)建工具腳本,可以自動(dòng)化這個(gè)過程,提高開發(fā)效率和應(yīng)用程序的穩(wěn)定性,感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解

    Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解

    分布式鎖和我們java基礎(chǔ)中學(xué)習(xí)到的synchronized略有不同,synchronized中我們的鎖是個(gè)對(duì)象,當(dāng)前系統(tǒng)部署在不同的服務(wù)實(shí)例上,單純使用synchronized或者lock已經(jīng)無法滿足對(duì)庫存一致性的判斷。本次主要講解基于rediss實(shí)現(xiàn)的分布式鎖
    2023-02-02
  • springboot?vue測(cè)試平臺(tái)接口定義及發(fā)送請(qǐng)求功能實(shí)現(xiàn)

    springboot?vue測(cè)試平臺(tái)接口定義及發(fā)送請(qǐng)求功能實(shí)現(xiàn)

    這篇文章主要為大家介紹了springboot+vue測(cè)試平臺(tái)接口定義及發(fā)送請(qǐng)求功能實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Java編碼輔助工具Lombok用法詳解

    Java編碼輔助工具Lombok用法詳解

    這篇文章主要介紹了Java編碼輔助工具Lombok用法詳解,可以使用Lombok來避免這種重復(fù)的操作,減少非核心代碼的臃腫,提高編碼效率,需要的朋友可以參考下
    2019-06-06
  • spring聲明式事務(wù)@Transactional底層工作原理

    spring聲明式事務(wù)@Transactional底層工作原理

    這篇文章主要為大家介紹分析spring聲明式事務(wù)@Transactional底層工作原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-02-02
  • 詳解Java String字符串獲取每一個(gè)字符及常用方法

    詳解Java String字符串獲取每一個(gè)字符及常用方法

    這篇文章主要介紹了詳解Java String字符串獲取每一個(gè)字符及常用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Spring Boot啟動(dòng)過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動(dòng)教程詳解

    Spring Boot啟動(dòng)過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和Sta

    這篇文章主要介紹了Spring Boot啟動(dòng)過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動(dòng)教程詳解,需要的朋友可以參考下
    2017-04-04
  • Java 如何使用正則表達(dá)式去除前導(dǎo)0

    Java 如何使用正則表達(dá)式去除前導(dǎo)0

    這篇文章主要介紹了Java 使用正則表達(dá)式去除前導(dǎo)0的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 關(guān)于Mybatis-Plus?Wrapper是否應(yīng)該出現(xiàn)在Servcie類中

    關(guān)于Mybatis-Plus?Wrapper是否應(yīng)該出現(xiàn)在Servcie類中

    最近在做代碼重構(gòu),代碼工程采用了Controller/Service/Dao分層架構(gòu),Dao層使用了Mybatis-Plus框架,本文帶領(lǐng)大家學(xué)習(xí)Mybatis-Plus?Wrapper應(yīng)該出現(xiàn)在Servcie類中嗎,需要的朋友可以參考下
    2023-05-05

最新評(píng)論

盈江县| 玛沁县| 乐山市| 靖边县| 柞水县| 邹城市| 辽阳市| 临沂市| 镇宁| 宁晋县| 德阳市| 锡林郭勒盟| 焉耆| 涞水县| 土默特左旗| 岫岩| 兴宁市| 翼城县| 上蔡县| 和平区| 旅游| 梁山县| 阳山县| 彝良县| 夏津县| 合山市| 高淳县| 浏阳市| 布拖县| 泾阳县| 太康县| 乐东| 辰溪县| 察隅县| 华安县| 汨罗市| 菏泽市| 五河县| 九台市| 阳城县| 高淳县|