溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java基于redis有序集合如何實現(xiàn)排行榜

發(fā)布時間:2020-09-14 10:04:31 來源:億速云 閱讀:447 作者:小新 欄目:關(guān)系型數(shù)據(jù)庫

小編給大家分享一下java基于redis有序集合如何實現(xiàn)排行榜,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

前言

排行榜作為互聯(lián)網(wǎng)應(yīng)用中幾乎必不可少的一個元素,能勾起人類自身對比的欲望,某寶中的商品銷量排行,店鋪信譽排行等,實現(xiàn)排行榜的方式也有很多種,可以使用快速排序算法 + 實現(xiàn)Comparator接口實現(xiàn)按某項權(quán)重排序,現(xiàn)在很多公司都在使用redis這個nosql數(shù)據(jù)庫實現(xiàn)排行榜的功能

基于redis實現(xiàn)排行榜

現(xiàn)在要做的是對公司進行排行,排行的標準是用戶對公司的搜索次數(shù),做一個前十公司的排行榜

1.相關(guān)的redis知識

與排行榜功能實現(xiàn)相關(guān)的redis數(shù)據(jù)結(jié)構(gòu)是sort set(有序集合)

關(guān)于sort set

我們知道set是一種集合,集合有一個特點就是無重復元素,sort set除了無重復元素外,還有一個特點就是有序性。

數(shù)據(jù)結(jié)構(gòu)組成:

  • key:sort set 的唯一標識
  • 權(quán)重:也叫分數(shù)(score)redis通過權(quán)重為集合中的元素進行升序排序(默認),權(quán)重可以重復
  • value:集合元素,元素不可重復
String(set key),double(權(quán)重),String(value)

sort set是通過哈希表實現(xiàn)的,所以添加,函數(shù),查找的時間復雜度都是O(1),每個集合可以存儲40多億個元素

基本命令

向集合中添加一個或多個元素

ZADD "KEY" SCORE "VALUE" [ SCORE "VALUE"]

效果:

MyRedis:0>ZADD test 1 "one""1"MyRedis:0>zadd test 4 "four" 5 "five""2"

獲取集合的元素數(shù)量

ZCARD "key"

效果

MyRedis:0>ZCARD test"5"

獲取指定元素分數(shù)(權(quán)重)

ZSCORE "KEY" "VALUE"

效果

MyRedis:0>ZSCORE "test" "one""2"

指定集合的指定元素增加指定分數(shù)

ZINCRBY "key" score "value"

效果:

MyRedis:0>ZSCORE "test" "one""2"MyRedis:0>ZINCRBY "test" 1 "one""3"MyRedis:0>ZSCORE "test" "one" "3"

獲取指定范圍的元素(默認按照分數(shù)|權(quán)重的升序排列)

ZRANGE "key" 開始下標 結(jié)束下標

效果

MyRedis:0>ZRANGE "test" 0 1
 1)  "two"
 2)  "one"

完成這個需求大概需要這么多命令,接下來開始實現(xiàn)我們的這個需求

2.springboot + redis實現(xiàn)

導入redis依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

編寫工具類

    //=============================== sort set =================================

    /**
     * 添加指定元素到有序集合中
     * @param key
     * @param score
     * @param value
     * @return
     */
    public boolean sortSetAdd(String key,double score,String value){
        try{
            return redisTemplate.opsForZSet().add(key,value,score);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 有序集合中對指定成員的分數(shù)加上增量 increment
     * @param key
     * @param value
     * @param i
     * @return
     */
    public double sortSetZincrby(String key,String value,double i){
        try {
            //返回新增元素后的分數(shù)
            return redisTemplate.opsForZSet().incrementScore(key, value, i);
        }catch(Exception e){
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 獲得有序集合指定范圍元素 (從大到小)
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set sortSetRange(String key,int start,int end){
        try {
            return redisTemplate.opsForZSet().reverseRange(key, start, end);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

業(yè)務(wù)實現(xiàn):

java基于redis有序集合如何實現(xiàn)排行榜

因為排行榜對實時性要求比較高,個人認為沒必要進行持久化到數(shù)據(jù)庫

    /**
     * 根據(jù)公司名找到指定公司
     * @param companyName
     * @return
     */
    @Override
    public AjaxResult selectCompanyName(String companyName) {
        Set<Object> set =  redisUtils.sGet("company");
        for(Object i : set){
            String json = JSONObject.toJSONString(i);
            JSONObject jsonObject = JSONObject.parseObject(json);
            if(jsonObject.getString("companyName").equals(companyName)){
                //搜索次數(shù) + 1
                redisUtils.sortSetZincrby("companyRank",companyName,1);
                log.info("直接緩存中返回");
                return new AjaxResult().ok(jsonObject);
            }
        }
        log.error("緩存中沒有,查數(shù)據(jù)庫");
        TbCommpanyExample tbCommpanyExample = new TbCommpanyExample();
        tbCommpanyExample.createCriteria().andCompanyNameEqualTo(companyName);
        List<TbCommpany> list = tbCommpanyMapper.selectByExample(tbCommpanyExample);
        if(list.size() != 0){
            //放入緩存中
            redisUtils.sSet("company",list.get(0));
            //數(shù)據(jù)庫中存在
            //搜索次數(shù) + 1
            redisUtils.sortSetZincrby("companyRank",companyName,1);
            log.info("sql");
            return new AjaxResult().ok(list.get(0));
        }else{
            return new AjaxResult().error("沒有找到該公司:"+companyName);
        }
    }

獲取排名

    /**
     * 獲得公司排行榜(前十)
     * @return
     */
    @Override
    public AjaxResult getCompanyRank() {
        Set set = redisUtils.sortSetRange("companyRank",0,9);
        if(set.size() == 0){
            return new AjaxResult().error("公司排行榜為空");
        }
        return new AjaxResult().ok(set);
    }

3.測試與總結(jié)

java基于redis有序集合如何實現(xiàn)排行榜

postman測試:

java基于redis有序集合如何實現(xiàn)排行榜

還有一個問題就是相同分數(shù)的排行問題

如果我希望A是先到的排在相同分數(shù)但是后到的B前邊,這個問題該如何解決呢?

要解決這個問題,我們可以考慮在分數(shù)中加入時間戳,計算公式為:

帶時間戳的分數(shù) = 實際分數(shù)*10000000000 + (9999999999 – timestamp)

這個帶時間的公司可以自己編寫,盡量縮減誤差

以上是java基于redis有序集合如何實現(xiàn)排行榜的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI