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

基于Redis?Set輕松實(shí)現(xiàn)簡單的抽獎(jiǎng)系統(tǒng)

 更新時(shí)間:2026年03月17日 11:07:35   作者:程序員牛奶  
Redis Set集合是無序且元素唯一的String類型數(shù)據(jù)結(jié)構(gòu),支持高效增刪查改操作,包括獲取所有值、判斷包含關(guān)系、計(jì)算交并差集等,底層基于Hash表實(shí)現(xiàn)O(1)時(shí)間復(fù)雜度,這篇文章主要介紹了基于Redis?Set輕松實(shí)現(xiàn)簡單的抽獎(jiǎng)系統(tǒng)的相關(guān)資料,需要的朋友可以參考下

基于 Redis Set 輕松搞定高并發(fā)抽獎(jiǎng)系統(tǒng)

想要從零手搓一個(gè)高性能的抽獎(jiǎng)系統(tǒng)?Redis 的 Set (集合)數(shù)據(jù)結(jié)構(gòu)絕對(duì)是你的不二之選。

它的特性和 Java 中的 HashSet 極其相似,天生自帶去重光環(huán)。這就意味著,無論一個(gè)用戶手速多快、瘋狂點(diǎn)擊了多少次參與,抽獎(jiǎng)池里也永遠(yuǎn)只有他的一個(gè)名字,完美避免了重復(fù)報(bào)名的問題。更棒的是,它底層隨機(jī)彈出元素的時(shí)間復(fù)雜度僅為 O(1)O(1)O(1),即使面對(duì)海量用戶的并發(fā)抽獎(jiǎng),也能輕松扛住壓力。

利用 Set 實(shí)現(xiàn)抽獎(jiǎng)系統(tǒng)的核心邏輯非常輕量,熟練掌握以下三個(gè)命令即可:

  • SADD key member1 member2 ... :向獎(jiǎng)池中添加一個(gè)或多個(gè)參與者。
  • SPOP key count:隨機(jī)從獎(jiǎng)池中抽出并移除指定數(shù)量的元素。非常適合“一等獎(jiǎng)”、“二等獎(jiǎng)”這種不允許重復(fù)中獎(jiǎng)的核心業(yè)務(wù)場(chǎng)景。
  • SRANDMEMBER key count:隨機(jī)從獎(jiǎng)池中獲取指定數(shù)量的元素,但不移除它們。適合“陽光普照獎(jiǎng)”、“參與獎(jiǎng)”這種允許重復(fù)中獎(jiǎng)的場(chǎng)景。

核心代碼實(shí)現(xiàn)

下面我們結(jié)合 Java (Spring Boot) 與 Redis,來落地這個(gè)抽獎(jiǎng)系統(tǒng)。

1. Controller 層:定義抽獎(jiǎng)接口

在這里我們定義了加入獎(jiǎng)池、抽取大獎(jiǎng)(不放回)以及抽取陽光獎(jiǎng)(可放回)的 API。

package com.example.redissetrandomget.lottery;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/lottery")
public class LotteryController {

    private final LotteryService lotteryService;

    public LotteryController(LotteryService lotteryService) {
        this.lotteryService = lotteryService;
    }

    // 加入抽獎(jiǎng)?wù)撸ㄖС峙浚?
    @RequestMapping(path = "/add", method = {RequestMethod.GET, RequestMethod.POST})
    public String add(@RequestParam String activityId, @RequestParam String[] userIds) {
        lotteryService.addParticipants(activityId, userIds);
        long remainCount = lotteryService.getRemainCount(activityId);
        return "成功加入獎(jiǎng)池!當(dāng)前獎(jiǎng)池總?cè)藬?shù):" + remainCount;
    }

    // 抽核心大獎(jiǎng)(抽完即踢出獎(jiǎng)池,絕對(duì)不重復(fù)中獎(jiǎng))
    @GetMapping("/drawGrand")
    public List<String> drawGrand(@RequestParam String activityId, @RequestParam long count) {
        return lotteryService.drawGrandPrize(activityId, count);
    }
    
    // 抽幸運(yùn)參與獎(jiǎng)(抽完保留在獎(jiǎng)池,下次還有機(jī)會(huì))
    @GetMapping("/drawSunshine")
    public List<String> drawSunshine(@RequestParam String activityId, @RequestParam long count) {
        return lotteryService.drawSunshinePrize(activityId, count);
    }
    
    // 查詢獎(jiǎng)池剩余人數(shù)
    @GetMapping("/remain")
    public long remain(@RequestParam String activityId) {
        return lotteryService.getRemainCount(activityId);
    }
}

2. Service 層:封裝 Redis 操作

Service 層主要負(fù)責(zé)與 Redis 進(jìn)行交互,并做了一些基礎(chǔ)的參數(shù)校驗(yàn)和清理工作,保證數(shù)據(jù)的健壯性。

package com.example.redissetrandomget.lottery;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;

@Service
public class LotteryService {

    private static final String LOTTERY_KEY_PREFIX = "lottery:activity:";
    private final StringRedisTemplate redisTemplate;

    public LotteryService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void addParticipants(String activityId, String... userIds) {
        redisTemplate.opsForSet().add(buildKey(activityId), normalizeUserIds(userIds));
    }

    // 使用 pop:隨機(jī)抽取并移除(適用于大獎(jiǎng))
    public List<String> drawGrandPrize(String activityId, long count) {
        validateCount(count);
        List<String> winners = redisTemplate.opsForSet().pop(buildKey(activityId), count);
        return winners != null ? winners : List.of();
    }

    // 使用 randomMembers:隨機(jī)抽取但不移除(適用于陽光普照獎(jiǎng))
    public List<String> drawSunshinePrize(String activityId, long count) {
        validateCount(count);
        List<String> winners = redisTemplate.opsForSet().randomMembers(buildKey(activityId), count);
        return winners != null ? winners : List.of();
    }

    public long getRemainCount(String activityId) {
        Long size = redisTemplate.opsForSet().size(buildKey(activityId));
        return size != null ? size : 0L;
    }

    public void joinLottery(String activityId, String... userIds) {
        addParticipants(activityId, userIds);
    }

    public List<String> drawWithoutRepeat(String activityId, long count) {
        return drawGrandPrize(activityId, count);
    }

    public List<String> drawWithRepeat(String activityId, long count) {
        return drawSunshinePrize(activityId, count);
    }

    public long participantCount(String activityId) {
        return getRemainCount(activityId);
    }

    // --- 私有輔助方法 ---

    private void validateCount(long count) {
        Assert.isTrue(count > 0, "抽獎(jiǎng)人數(shù)必須大于 0");
    }

    private String buildKey(String activityId) {
        Assert.hasText(activityId, "活動(dòng) ID 不能為空");
        return LOTTERY_KEY_PREFIX + activityId.trim();
    }

    private String[] normalizeUserIds(String[] userIds) {
        Assert.notEmpty(userIds, "用戶列表不能為空");

        String[] normalizedUserIds = Arrays.stream(userIds)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .distinct()
                .toArray(String[]::new);

        Assert.notEmpty(normalizedUserIds, "過濾后沒有合法的用戶 ID");
        return normalizedUserIds;
    }
}

接口測(cè)試與驗(yàn)證

代碼準(zhǔn)備就緒,我們來模擬一次真實(shí)的抽獎(jiǎng)流程。

首先,我們通過接口向活動(dòng) 2026 的獎(jiǎng)池中加入 5 名測(cè)試用戶。你可以在 Redis 客戶端中使用 SCARD lottery:activity:2026 命令來驗(yàn)證獎(jiǎng)池內(nèi)的人數(shù),確認(rèn) 5 人已成功入場(chǎng):

測(cè)試一:抽取大獎(jiǎng)(不放回)

我們先來測(cè)試一下抽取 2 名一等獎(jiǎng)用戶。調(diào)用 drawGrand 接口:

HTTP

GET http://localhost:8080/api/lottery/drawGrand?activityId=2026&count=2

接口成功返回了 3 號(hào)和 5 號(hào)用戶。由于使用的是 SPOP 命令,這兩個(gè)幸運(yùn)兒已經(jīng)被移出獎(jiǎng)池,后續(xù)的抽獎(jiǎng)中絕不會(huì)再出現(xiàn)他們的身影。

HTTP/1.1 200 
Content-Type: application/json
Date: Fri, 13 Mar 2026 08:54:20 GMT

[
  "3",
  "5"
]

測(cè)試二:抽取幸運(yùn)參與獎(jiǎng)(可放回)

接下來,我們測(cè)試抽取 2 名陽光普照獎(jiǎng)。調(diào)用 drawSunshine 接口:

HTTP

GET http://localhost:8080/api/lottery/drawSunshine?activityId=2026&count=2

查看返回結(jié)果,我們發(fā)現(xiàn) 2 號(hào)用戶被抽中了兩次!這正是 SRANDMEMBER 的特性:隨機(jī)抽取元素但保留在原集合中,因此同一個(gè)用戶在同一輪或不同輪次中都有可能重復(fù)中獎(jiǎng)。

JSON

HTTP/1.1 200 
Content-Type: application/json
Date: Fri, 13 Mar 2026 08:56:28 GMT

[
  "2",
  "2"
]

總結(jié) 

到此這篇關(guān)于基于Redis Set輕松實(shí)現(xiàn)簡單的抽獎(jiǎng)系統(tǒng)的文章就介紹到這了,更多相關(guān)Redis Set抽獎(jiǎng)系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • RedisJSON中JSON.SET的用法小結(jié)

    RedisJSON中JSON.SET的用法小結(jié)

    本文全面解析 RedisJSON 插件的核心指令JSON.SET,涵蓋語法規(guī)則、典型用法、易錯(cuò)點(diǎn)和性能優(yōu)化建議,感興趣的可以了解一下
    2025-11-11
  • Redis內(nèi)存碎片處理實(shí)例詳解

    Redis內(nèi)存碎片處理實(shí)例詳解

    內(nèi)存碎片是redis服務(wù)中分配器分配存儲(chǔ)對(duì)象內(nèi)存的時(shí)產(chǎn)生的,下面這篇文章主要給大家介紹了關(guān)于Redis內(nèi)存碎片處理的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • Redis+threading實(shí)現(xiàn)多線程消息隊(duì)列的使用示例

    Redis+threading實(shí)現(xiàn)多線程消息隊(duì)列的使用示例

    Redis多線程消息隊(duì)列是一種使用Redis作為存儲(chǔ)后端的消息隊(duì)列實(shí)現(xiàn),它利用Redis的線程并發(fā)處理能力來提高消息隊(duì)列的處理效率,本文主要介紹了Redis+threading實(shí)現(xiàn)多線程消息隊(duì)列的使用示例,感興趣的可以了解一下
    2023-12-12
  • redis客戶端實(shí)現(xiàn)高可用讀寫分離的方式詳解

    redis客戶端實(shí)現(xiàn)高可用讀寫分離的方式詳解

    基于sentienl 獲取和動(dòng)態(tài)感知 master、slaves節(jié)點(diǎn)信息的變化,我們的讀寫分離客戶端就能具備高可用+動(dòng)態(tài)擴(kuò)容感知能力了,接下來通過本文給大家分享redis客戶端實(shí)現(xiàn)高可用讀寫分離的方式,感興趣的朋友一起看看吧
    2021-07-07
  • 使用Redis實(shí)現(xiàn)記錄訪問次數(shù)的三種方案

    使用Redis實(shí)現(xiàn)記錄訪問次數(shù)的三種方案

    這篇文章主要介紹了使用Redis實(shí)現(xiàn)記錄訪問次數(shù)的三種方案,文中通過代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-09-09
  • Redis 數(shù)據(jù)類型Streams詳解

    Redis 數(shù)據(jù)類型Streams詳解

    Redis Streams是Redis 5.0新增的數(shù)據(jù)類型,提供了一種日志結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)方式,這種類型適合用于構(gòu)建消息隊(duì)列、事件日志和處理時(shí)間序列數(shù)據(jù)的應(yīng)用,本文介紹Redis 數(shù)據(jù)類型Streams相關(guān)知識(shí),感興趣的朋友一起看看吧
    2024-10-10
  • springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案)

    springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案)

    redis基本上是高并發(fā)場(chǎng)景上會(huì)用到的一個(gè)高性能的key-value數(shù)據(jù)庫,屬于nosql類型,一般用作于緩存,一般是結(jié)合數(shù)據(jù)庫一塊使用的,但是在使用的過程中可能會(huì)出現(xiàn)異常的問題,這篇文章主要介紹了springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案),需要的朋友可以參考下
    2025-05-05
  • 基于Redis實(shí)現(xiàn)雙加密Token的示例代碼

    基于Redis實(shí)現(xiàn)雙加密Token的示例代碼

    在現(xiàn)代分布式系統(tǒng)中,Token管理是身份驗(yàn)證和授權(quán)的核心部分,本文將深入分析一個(gè)基于Redis的Token管理實(shí)現(xiàn),探討其設(shè)計(jì)思路、關(guān)鍵代碼邏輯以及實(shí)現(xiàn)細(xì)節(jié),通過對(duì)源碼的逐層剖析,幫助讀者更好地理解Token管理的實(shí)現(xiàn)原理,需要的朋友可以參考下
    2025-01-01
  • Redis設(shè)置開機(jī)自啟動(dòng)全過程

    Redis設(shè)置開機(jī)自啟動(dòng)全過程

    這篇文章主要介紹了Redis設(shè)置開機(jī)自啟動(dòng)全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-10-10
  • 如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性

    如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性

    這篇文章主要介紹了如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性,文中舉了兩個(gè)場(chǎng)景例子介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05

最新評(píng)論

东乡| 桑日县| 丹凤县| 南投县| 台湾省| 琼海市| 海安县| 化隆| 京山县| 永嘉县| 浦东新区| 建平县| 冕宁县| 昌图县| 商城县| 沙湾县| 方正县| 南华县| 佳木斯市| 黔南| 化州市| 三原县| 同江市| 莆田市| 宜阳县| 洞口县| 丹寨县| SHOW| 灯塔市| 嘉兴市| 武平县| 夏津县| 师宗县| 无极县| 金平| 静海县| 藁城市| 托里县| 弥勒县| 安达市| 葫芦岛市|