手把手教你使用redis實現(xiàn)排行榜功能
一、需求背景
最近項目需要做排行榜功能,實現(xiàn)員工邀請用戶注冊排行榜,要求是實時更新,查詢要快。員工所屬支行、二級行、省行,界面要根據(jù)條件顯示排名數(shù)據(jù)。效果如下圖所示:
原型圖展示比較隨意,用excel隨便寫了一下,湊合著看。
二、實現(xiàn)思路
1、利用數(shù)據(jù)庫
建一張統(tǒng)計表,字段包括:邀請人、邀請人所屬支行、邀請人所屬二級行、被邀請人、注冊時間等關(guān)鍵信息,用于sql統(tǒng)計排名,根據(jù)條件使用group by相關(guān)字段,比較簡單,這個大家應(yīng)該清楚。
數(shù)據(jù)量小,統(tǒng)計效率還可以。但是支行下有十幾萬員工,一個員工邀請10個就百萬數(shù)據(jù),如果更多,數(shù)據(jù)就更大了,統(tǒng)計效率不高。下面重點討論用第二種方式實現(xiàn)。
2、利用redis
我們都知道redis基于內(nèi)存實現(xiàn)的,查詢效率極高,且支持多種數(shù)據(jù)類型,其中zset是本次實現(xiàn)功能的關(guān)鍵。
- ZSet也是String類型元素的集合,且不允許重復(fù)的成員;
- 不同的是每個元素都會關(guān)聯(lián)一個double類型的分數(shù),剛好也是我們需要的邀請用戶數(shù);
- 通過分數(shù)來為集合中的成員進行排序。ZSet的成員是唯一的,但分數(shù)(score)卻可以重復(fù);
基于上面的特性,滿足我們本次的需求。好了,說了一大堆廢話,下面將進入正題。
首先,捋一下查詢條件,根據(jù)前面的效果圖,可以看出有以下幾種情況:
- 二級行的全部排名以及日周月榜排名
- 支行在省行的全部排名以及日周月榜排名
- 支行在二級行的全部排名以及日周月榜排名
- 員工在省行的全部排名以及日周月榜排名
- 員工在二級行的全部排名以及日周月榜排名
- 員工在支行的全部排名以及日周月榜排名
基于redis的Zset函數(shù)incrementScore,我們很快就能發(fā)現(xiàn),其實實現(xiàn)各個排名,只要把key規(guī)定好即可,例如:
- 員工在省行的全部排名key,可以設(shè)置為 rank:employee:省行
- 員工在省行的日排行榜key,可以設(shè)置為 rank:emploee:省行:當天日期
下面我們來實現(xiàn)其中的上面的兩個排行:
String key = "rank:employee:廣東省"; redisTemplate.opsForZSet().incrementScore(key, "張三", 10); redisTemplate.opsForZSet().incrementScore(key, "李四", 8); redisTemplate.opsForZSet().incrementScore(key, "王五", 5);
執(zhí)行完后,redis會保存為如下數(shù)據(jù):
這樣的話,有了上面的數(shù)據(jù),可以用redis的提供的函數(shù)把排行榜查出來,代碼如下:
String key = "rank:employee:廣東省"; Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, -1); JSONArray jsonArray = JSONObject.parseArray(JSONObject.toJSONString(set)); for(int i = 0, size = jsonArray.size(); i < size; i++) { JSONObject o = JSONObject.parseObject(jsonArray.get(i).toString()); System.out.println("員工:" + o.getString("value") + ", 邀請人數(shù):" + o.getLongValue("score")); }
reverseRangeWithScores方法接收三個參數(shù),第一個是key,后面兩個是分頁查詢,起始是從0開始,-1表示全部,如果設(shè)置為0,4,那么就是查詢前5條記錄,查出結(jié)果如下:
以上就實現(xiàn)了員工在省行的排名。類似的,員工要實現(xiàn)在省行的日榜,代碼如下:
String key = "rank:employee:廣東省:2022-09-01"; redisTemplate.opsForZSet().incrementScore(key, "張三", 10); redisTemplate.opsForZSet().incrementScore(key, "李四", 8); redisTemplate.opsForZSet().incrementScore(key, "王五", 5);
執(zhí)行完后,redis會保存為如下數(shù)據(jù)(這里我多設(shè)置了前一天的數(shù)據(jù)):
一樣的,用 reverseRangeWithScores方法可以把上面的結(jié)果查出來。
至于周榜、月榜,可以把每一天的數(shù)據(jù)累加起來,再做個排名,redis已經(jīng)幫我們實現(xiàn)了這個功能,代碼如下:
Date date = DateUtil.date(); //獲取本周的第一天 DateTime beginOfWeek = DateUtil.beginOfWeek(date); //到今天一共有幾天 long diffDay = DateUtil.between(date, beginOfWeek, DateUnit.DAY) + 1; List<String> keys = new ArrayList<>(); for(int i = 0; i < diffDay; i++) { //把需要查詢的天數(shù)放一起 keys.add("rank:employee:廣東省:" + DateUtil.formatDate(DateUtil.offsetDay(beginOfWeek, i))); } //redis使用unionAndStore做合并,將結(jié)果集放在另一個的key,也就是第三個參數(shù) redisTemplate.opsForZSet().unionAndStore("weekRank", keys, "employeeRankWeek"); //查詢結(jié)果集用employeeRankWeek這個key Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().reverseRangeWithScores("employeeRankWeek", 0, -1); JSONArray jsonArray = JSONObject.parseArray(JSONObject.toJSONString(set)); for(int i = 0, size = jsonArray.size(); i < size; i++) { JSONObject o = JSONObject.parseObject(jsonArray.get(i).toString()); System.out.println("員工:" + o.getString("value") + ", 本周邀請人數(shù):" + o.getLongValue("score")); }
注意代碼里面說的,redis會把結(jié)果合并到另一個key,在redis上也可以看到,如下圖:
查出來的結(jié)果如下圖:
其實我們會發(fā)現(xiàn),本周榜、本月榜無需保存每一天的數(shù)據(jù),只要把key設(shè)置為本周或本月的第一天就可以,因為添加數(shù)據(jù)的那一刻就知道是哪一周或哪月了。
例如:key = rank:employee:廣東省:2022-08-29,8月29日是本周的第一天,無論你在接下來一周內(nèi)邀請多少人,都是在本周內(nèi)完成的,在這個基礎(chǔ)上累加邀請數(shù)量即可。本月榜的邏輯也是一樣。
查詢的時候,獲取當前時間本周或本月的第一天,就可以實現(xiàn)本周、本月排行了。
String key = "rank:employee:廣東省"; Date date = DateUtil.date(); String week = DateUtil.formatDate(DateUtil.beginOfWeek(date)); String month = DateUtil.formatDate(DateUtil.beginOfMonth(date)); //周榜 redisTemplate.opsForZSet().incrementScore(key+":week:"+week, "張三", 17); //月榜 redisTemplate.opsForZSet().incrementScore(key+":month:"+month, "張三", 17);
當然了,如果要查詢歷史的排行,這種設(shè)計就滿足不了了,還是要保存每天的數(shù)據(jù)才行。
總結(jié)
到此這篇關(guān)于使用redis實現(xiàn)排行榜功能的文章就介紹到這了,更多相關(guān)redis實現(xiàn)排行榜功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis實現(xiàn)限流器的三種方法(小結(jié))
本文主要介紹了Redis實現(xiàn)限流器的三種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Redis生成分布式系統(tǒng)全局唯一ID的實現(xiàn)
在互聯(lián)網(wǎng)系統(tǒng)中,并發(fā)越大的系統(tǒng),數(shù)據(jù)就越大,數(shù)據(jù)越大就越需要分布式,本文主要介紹了Redis生成分布式系統(tǒng)全局唯一ID的實現(xiàn),感興趣的可以了解一下2021-10-10Redis全文搜索教程之創(chuàng)建索引并關(guān)聯(lián)源數(shù)據(jù)的教程
RediSearch提供了一種簡單快速的方法對 hash 或者 json 類型數(shù)據(jù)的任何字段建立二級索引,然后就可以對被索引的 hash 或者 json 類型數(shù)據(jù)字段進行搜索和聚合操作,這篇文章主要介紹了Redis全文搜索教程之創(chuàng)建索引并關(guān)聯(lián)源數(shù)據(jù),需要的朋友可以參考下2023-12-12詳解redis desktop manager安裝及連接方式
這篇文章主要介紹了redis desktop manager安裝及連接方式,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09如何使用Redis實現(xiàn)電商系統(tǒng)的庫存扣減
在日常開發(fā)中有很多地方都有類似扣減庫存的操作,本文主要介紹了如何使用Redis實現(xiàn)電商系統(tǒng)的庫存扣減,具有一定的參考價值,感興趣的可以了解一下2022-01-01