亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

在處理計數(shù)業(yè)務(wù)時,我們一般會使用一個數(shù)據(jù)結(jié)構(gòu),既是集合又可以保證唯一性,所以我們會選擇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();
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

下面是修改后的點(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 用戶順序完全匹配。因為 Redis 中的 ZSet 是有順序的,top5 會按照點(diǎn)贊數(shù)量進(jìn)行排序。

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

總結(jié)

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

相關(guān)文章

  • Redis 中的布隆過濾器的實現(xiàn)

    Redis 中的布隆過濾器的實現(xiàn)

    這篇文章主要介紹了Redis 中的布隆過濾器的實現(xiàn),詳細(xì)的介紹了什么是布隆過濾器以及如何實現(xiàn),非常具有實用價值,需要的朋友可以參考下
    2018-10-10
  • Redis實現(xiàn)和數(shù)據(jù)庫的數(shù)據(jù)同步

    Redis實現(xiàn)和數(shù)據(jù)庫的數(shù)據(jù)同步

    本文介紹了Redis與傳統(tǒng)數(shù)據(jù)庫數(shù)據(jù)同步的幾種常見方法,包括CacheAside、WriteThrough、WriteBehind,以及如何通過分布式事務(wù)、樂觀鎖、數(shù)據(jù)過期策略和消息隊列來解決數(shù)據(jù)一致性問題,每種方法都有其適用場景和優(yōu)缺點(diǎn),需要根據(jù)具體需求進(jìn)行選擇
    2025-01-01
  • 硬核!15張圖解Redis為什么這么快(推薦)

    硬核!15張圖解Redis為什么這么快(推薦)

    作為一名服務(wù)端工程師,工作中你肯定和 Redis 打過交道。Redis為什么快,這點(diǎn)想必你也知道,至少為了面試也做過準(zhǔn)備,今天通過本文給大家介紹下,感興趣的朋友一起看看吧
    2020-10-10
  • redis通過位圖法記錄在線用戶的狀態(tài)詳解

    redis通過位圖法記錄在線用戶的狀態(tài)詳解

    這篇文章主要給大家介紹了關(guān)于redis如何通過位圖法記錄在線用戶的狀態(tài)的相關(guān)資料,文中先對位圖進(jìn)行了一個簡單的介紹,而后通過示例代碼將實現(xiàn)的方法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • Redis設(shè)置Hash數(shù)據(jù)類型的過期時間

    Redis設(shè)置Hash數(shù)據(jù)類型的過期時間

    在Redis中,我們可以使用Hash數(shù)據(jù)結(jié)構(gòu)來存儲一組鍵值對,而有時候,我們可能需要設(shè)置這些鍵值對的過期時間,本文主要介紹了Redis設(shè)置Hash數(shù)據(jù)類型的過期時間,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 詳解redis緩存與數(shù)據(jù)庫一致性問題解決

    詳解redis緩存與數(shù)據(jù)庫一致性問題解決

    這篇文章主要介紹了詳解redis緩存與數(shù)據(jù)庫一致性問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Redis Cluster集群收縮主從節(jié)點(diǎn)詳細(xì)教程

    Redis Cluster集群收縮主從節(jié)點(diǎn)詳細(xì)教程

    集群收縮的源端就是要下線的主節(jié)點(diǎn),目標(biāo)端就是在線的主節(jié)點(diǎn),這篇文章主要介紹了Redis Cluster集群收縮主從節(jié)點(diǎn)詳細(xì)教程,需要的朋友可以參考下
    2021-11-11
  • Redis數(shù)據(jù)過期策略的實現(xiàn)詳解

    Redis數(shù)據(jù)過期策略的實現(xiàn)詳解

    最近項目當(dāng)中遇到一個需求場景,需要清空一些存放在Redis的數(shù)據(jù),本文對Redis的過期機(jī)制簡單的講解一下,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Redis優(yōu)化token校驗主動失效的實現(xiàn)方案

    Redis優(yōu)化token校驗主動失效的實現(xiàn)方案

    在普通的token頒發(fā)和校驗中 當(dāng)用戶發(fā)現(xiàn)自己賬號和密碼被暴露了時修改了登錄密碼后舊的token仍然可以通過系統(tǒng)校驗直至token到達(dá)失效時間,所以系統(tǒng)需要token主動失效的一種能力,所以本文給大家介紹了Redis優(yōu)化token校驗主動失效的實現(xiàn)方案,需要的朋友可以參考下
    2024-03-03
  • 使用Redis存儲SpringBoot項目中Session的詳細(xì)步驟

    使用Redis存儲SpringBoot項目中Session的詳細(xì)步驟

    在開發(fā)Spring Boot項目時,我們通常會遇到如何高效管理Session的問題,默認(rèn)情況下,Spring Boot會將Session存儲在內(nèi)存中,今天,我們將學(xué)習(xí)如何將Session存儲從內(nèi)存切換到Redis,并驗證配置是否成功,需要的朋友可以參考下
    2024-06-06

最新評論