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

Redis如何使用zset處理排行榜和計(jì)數(shù)問題

 更新時(shí)間:2025年02月05日 17:14:49   作者:記得開心一點(diǎn)嘛  
Redis的ZSET數(shù)據(jù)結(jié)構(gòu)非常適合處理排行榜和計(jì)數(shù)問題,它可以在高并發(fā)的點(diǎn)贊業(yè)務(wù)中高效地管理點(diǎn)贊的排名,并且由于ZSET的排序特性,可以輕松實(shí)現(xiàn)根據(jù)點(diǎn)贊數(shù)實(shí)時(shí)排序的功能

Redis使用zset處理排行榜和計(jì)數(shù)

在處理計(jì)數(shù)業(yè)務(wù)時(shí),我們一般會(huì)使用一個(gè)數(shù)據(jù)結(jié)構(gòu),既是集合又可以保證唯一性,所以我們會(huì)選擇Redis中的set集合:

業(yè)務(wù)邏輯

用戶點(diǎn)擊點(diǎn)贊按鈕,需要再set集合內(nèi)判斷是否已點(diǎn)贊,未點(diǎn)贊則需要將點(diǎn)贊數(shù)+1并保存用戶信息到集合中,已點(diǎn)贊則需要將數(shù)據(jù)庫點(diǎn)贊數(shù)-1并移除set集合中的用戶。

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result likeBlog(Long id) {
        // 獲取登錄用戶
        Long userId = UserHolder.getUser().getId();
        // 判斷當(dāng)前登錄用戶是否已經(jīng)點(diǎn)贊
        String key = "blog:like:" + id;
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        if(BooleanUtil.isFalse(isMember)){
            // 未點(diǎn)贊
            // 數(shù)據(jù)庫點(diǎn)贊數(shù)+1
            boolean isSuccess = update().setSql("like = like + 1").eq("id",id).update();
            // 保存用戶到Redis集合中
            if(isSuccess){
                stringRedisTemplate.opsForSet().add(key, userId.toString());
            }
        } else {
            // 已點(diǎn)贊,取消點(diǎn)贊
            // 數(shù)據(jù)庫點(diǎn)贊數(shù)-1
            boolean isSuccess = update().setSql("like = like - 1").eq("id",id).update();
            // 移除set集合中的用戶
            stringRedisTemplate.opsForSet().remove(key, userId.toString());
        }
        return Result.ok();
    }
}

那么我們想要實(shí)現(xiàn)按照點(diǎn)贊時(shí)間的先后順序排序,返回Top5的用戶,這個(gè)時(shí)候set無法保證數(shù)據(jù)有序,所以我們需要換一個(gè)數(shù)據(jù)結(jié)構(gòu)滿足業(yè)務(wù)需求:

Redis 的 ZSET(有序集合) 是一個(gè)非常適合用于處理 排行榜計(jì)數(shù)問題 的數(shù)據(jù)結(jié)構(gòu)。

在高并發(fā)的點(diǎn)贊業(yè)務(wù)中,使用 ZSET 可以幫助我們高效地管理點(diǎn)贊的排名,并且由于 ZSET 的排序特性,我們可以輕松實(shí)現(xiàn)根據(jù)點(diǎn)贊數(shù)實(shí)時(shí)排序的功能。

ZSET 數(shù)據(jù)結(jié)構(gòu)

Redis 的 ZSET 是一個(gè)集合,它的每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) 分?jǐn)?shù)(score),這個(gè)分?jǐn)?shù)決定了元素在集合中的排序。ZSET 保證集合中的元素是按分?jǐn)?shù)排序的,并且可以在 O(log(N)) 的時(shí)間復(fù)雜度內(nèi)進(jìn)行添加、刪除和查找操作。

在高并發(fā)的點(diǎn)贊業(yè)務(wù)中,ZSET 可以幫助我們輕松地進(jìn)行以下幾項(xiàng)操作:

  • 記錄每個(gè)用戶對(duì)某個(gè)內(nèi)容(如文章、評(píng)論等)的點(diǎn)贊數(shù)。
  • 通過分?jǐn)?shù)進(jìn)行實(shí)時(shí)排序,獲取點(diǎn)贊數(shù)最多的內(nèi)容

優(yōu)化高并發(fā)的點(diǎn)贊操作

高并發(fā)情況下,當(dāng)多個(gè)用戶同時(shí)對(duì)某個(gè)內(nèi)容進(jìn)行點(diǎn)贊時(shí),我們需要高效地更新該內(nèi)容的點(diǎn)贊數(shù),并保證數(shù)據(jù)一致性。ZSET 提供了很好的支持,具體步驟如下:

  • 用戶點(diǎn)贊操作:使用 ZINCRBY 命令來對(duì)某個(gè)元素的分?jǐn)?shù)進(jìn)行增量操作,表示對(duì)該內(nèi)容的點(diǎn)贊數(shù)增加。
  • 查看點(diǎn)贊數(shù):可以通過 ZSCORE 命令獲取某個(gè)內(nèi)容的當(dāng)前點(diǎn)贊數(shù)。
  • 查看排行榜:使用 ZRANGEZREVRANGE 命令來獲取點(diǎn)贊數(shù)排名前 N 的內(nèi)容,按分?jǐn)?shù)進(jìn)行排序。

ZSET 結(jié)構(gòu)設(shè)計(jì)

  • key:表示某個(gè)內(nèi)容的點(diǎn)贊的 id。
  • value:表示點(diǎn)贊用戶的 id。
  • score:根據(jù)點(diǎn)贊時(shí)間排序。

下面是修改后的點(diǎn)贊邏輯:

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result likeBlog(Long id) {
        // 獲取登錄用戶
        Long userId = UserHolder.getUser().getId();
        // 判斷當(dāng)前登錄用戶是否已經(jīng)點(diǎn)贊
        String key = "blog:like:" + id;
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        if(score == null){
            // 未點(diǎn)贊
            // 數(shù)據(jù)庫點(diǎn)贊數(shù)+1
            boolean isSuccess = update().setSql("like = like + 1").eq("id",id).update();
            // 保存用戶到Redis集合中
            if(isSuccess){
                stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
            }
        } else {
            // 已點(diǎn)贊,取消點(diǎn)贊
            // 數(shù)據(jù)庫點(diǎn)贊數(shù)-1
            boolean isSuccess = update().setSql("like = like - 1").eq("id",id).update();
            // 移除set集合中的用戶
            stringRedisTemplate.opsForZSet().remove(key, userId.toString());
        }
        return Result.ok();
    }
}

而點(diǎn)贊排行榜代碼如下:

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryBlogLikes(Long id) {
        String key = "blog:like:" + id;
        // 查詢top5的點(diǎn)贊用戶 zrange key 0 4
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (top5 == null || top5.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        // 解析出集合中的用戶的id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        // 根據(jù)id查詢用戶,并將類型由User轉(zhuǎn)為UserDTO,隨后轉(zhuǎn)換為List集合
        String idStr = StrUtil.join(",",ids);
//        List<UserDTO> userDTOs = userService.listByIds(ids).stream()
//                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
//                .collect(Collectors.toList());
        List<UserDTO> userDTOs = userService.query()
                .in("id",ids).last("order by field(id," + idStr +")").list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(userDTOs);
    }
}

使用

userService.query().in("id", ids).last("order by field(id," + idStr + ")") 

來查詢用戶信息,并且使用 order by field(id, ...) 語句來保證查詢結(jié)果的順序與 top5 中的用戶順序一致。

這里的 order by field(id, ...) 是關(guān)鍵,它確保了從數(shù)據(jù)庫返回的數(shù)據(jù)順序和 Redis 返回的 top5 用戶順序完全匹配。因?yàn)?Redis 中的 ZSet 是有順序的,top5 會(huì)按照點(diǎn)贊數(shù)量進(jìn)行排序。

如果直接使用 listByIds 方法,可能會(huì)導(dǎo)致結(jié)果順序不一致。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Redis存儲(chǔ)斷點(diǎn)續(xù)傳文件狀態(tài)的最佳實(shí)踐

    Redis存儲(chǔ)斷點(diǎn)續(xù)傳文件狀態(tài)的最佳實(shí)踐

    在斷點(diǎn)續(xù)傳系統(tǒng)中,如何高效地存儲(chǔ)和更新文件上傳狀態(tài)是關(guān)鍵,得益于 Redis 高效的內(nèi)存操作和多種數(shù)據(jù)結(jié)構(gòu)的支持,它非常適合用于存儲(chǔ)上傳過程中的臨時(shí)狀態(tài)信息,下面,我們將探討如何利用 Redis 實(shí)現(xiàn)文件上傳狀態(tài)的存儲(chǔ),需要的朋友可以參考下
    2024-12-12
  • redis鎖機(jī)制介紹與實(shí)例

    redis鎖機(jī)制介紹與實(shí)例

    今天小編就為大家分享一篇關(guān)于redis鎖機(jī)制介紹與實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Redis的主從復(fù)制機(jī)制以及主從復(fù)制的實(shí)現(xiàn)方法

    Redis的主從復(fù)制機(jī)制以及主從復(fù)制的實(shí)現(xiàn)方法

    本文介紹了Redis的主從復(fù)制機(jī)制,包括異步同步、快照同步、增量同步以及如何配置主從服務(wù)器,主從同步包括增量同步和快照同步兩種方式,增量同步通過指令流實(shí)現(xiàn),快照同步通過快照文件傳輸數(shù)據(jù),主從復(fù)制可以提高系統(tǒng)的穩(wěn)定性和性能,但也需要合理配置資源以避免資源浪費(fèi)
    2026-02-02
  • redis key過期監(jiān)聽的實(shí)現(xiàn)示例

    redis key過期監(jiān)聽的實(shí)現(xiàn)示例

    在Redis中,我們可以為Key設(shè)置過期時(shí)間,當(dāng)Key的過期時(shí)間到達(dá)后,Redis會(huì)自動(dòng)將該Key標(biāo)記為已失效,本文就來介紹一下redis key過期監(jiān)聽的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-03-03
  • redis使用不當(dāng)導(dǎo)致應(yīng)用卡死bug的過程解析

    redis使用不當(dāng)導(dǎo)致應(yīng)用卡死bug的過程解析

    本文主要記一次找因redis使用不當(dāng)導(dǎo)致應(yīng)用卡死bug的過程,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • Redis 高可用與集群原理深度解析

    Redis 高可用與集群原理深度解析

    Redis高可用機(jī)制涵蓋主從復(fù)制、哨兵自動(dòng)故障轉(zhuǎn)移及Cluster集群分片與高可用,依賴Gossip協(xié)議實(shí)現(xiàn)節(jié)點(diǎn)通信與狀態(tài)同步,源碼解析了故障轉(zhuǎn)移流程,為后續(xù)持久化內(nèi)容奠定基礎(chǔ),本文給大家介紹Redis高可用與集群原理,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • 基于Redis實(shí)現(xiàn)抽獎(jiǎng)功能及問題小結(jié)

    基于Redis實(shí)現(xiàn)抽獎(jiǎng)功能及問題小結(jié)

    這篇文章主要介紹了基于Redis實(shí)現(xiàn)抽獎(jiǎng)功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Redis之十大數(shù)據(jù)類型解讀

    Redis之十大數(shù)據(jù)類型解讀

    文章主要介紹了Redis的基本操作命令,包括key管理、string、list、hash、set、zset、bitmap、HyperLogLog、GEO、stream、bitfields等數(shù)據(jù)類型的操作方法和應(yīng)用場(chǎng)景
    2026-04-04
  • Redis設(shè)置鍵的生存時(shí)間或過期時(shí)間的方法詳解

    Redis設(shè)置鍵的生存時(shí)間或過期時(shí)間的方法詳解

    這篇文章主要介紹了Redis如何設(shè)置鍵的生存時(shí)間或過期時(shí)間,通過EXPIRE命令或者PEXIPIRE命令,客戶端可以以秒或者毫秒精度為數(shù)據(jù)庫中的某個(gè)鍵設(shè)置生存時(shí)間,文中有詳細(xì)的代碼供供大家參考,需要的朋友可以參考下
    2024-03-03
  • Redis Stat的安裝指南

    Redis Stat的安裝指南

    這篇文章主要介紹了Redis Stat的安裝指南的相關(guān)資料,需要的朋友可以參考下
    2016-04-04

最新評(píng)論

保山市| 鲁山县| 海宁市| 台安县| 镇平县| 阿瓦提县| 镇雄县| 金塔县| 新沂市| 安宁市| 从化市| 承德市| 桃园市| 淮北市| 凌云县| 泽库县| 千阳县| 定远县| 全州县| 万安县| 丰原市| 白水县| 手机| 钦州市| 军事| 阿巴嘎旗| 普格县| 运城市| 平阴县| 通山县| 桐柏县| 江口县| 寿光市| 寻乌县| 陇西县| 龙泉市| 山西省| 昌都县| 芷江| 抚宁县| 翁牛特旗|