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

Redis中ZSet數(shù)據(jù)結(jié)構(gòu)與滑動(dòng)窗口應(yīng)用實(shí)現(xiàn)

 更新時(shí)間:2025年07月30日 09:53:51   作者:葵續(xù)淺笑  
Redis?ZSET融合哈希表與跳躍表,支持O(1)成員查詢和O(logN)排序,適用于排行榜及滑動(dòng)時(shí)間窗口限流,下面就來(lái)介紹一下Redis中ZSet數(shù)據(jù)結(jié)構(gòu)與滑動(dòng)窗口應(yīng)用,感興趣的可以了解一下

Redis 的 ZSET(有序集合) 是一種結(jié)合了 哈希表跳躍表(Skip List) 的混合數(shù)據(jù)結(jié)構(gòu),既能實(shí)現(xiàn) O(1) 復(fù)雜度的成員存在性判斷,又能以 O(logN) 復(fù)雜度維護(hù)有序性。

Redis ZSET 數(shù)據(jù)存儲(chǔ)機(jī)制

ZSET 有兩種實(shí)現(xiàn)機(jī)制:

SkipList + HashTable

數(shù)據(jù)實(shí)際上是同時(shí)存在于兩個(gè)數(shù)據(jù)結(jié)構(gòu)中的

跳表(SkipList)

  • score 排序存儲(chǔ) member
  • 支持范圍查詢(ZRANGE 等命令)
  • 維護(hù)成員的有序性

哈希表(HashTable)

  • 存儲(chǔ) member -> score 的映射
  • 用于快速判斷成員是否存在(O(1) 復(fù)雜度)
  • 直接獲取成員的分?jǐn)?shù)(ZSCORE 命令)

ZipList

ZipList:對(duì)于小型有序集合(元素少且 member ?。?,Redis 會(huì)使用 ziplist 編碼來(lái)節(jié)省內(nèi)存,只有當(dāng)元素?cái)?shù)量或大小超過(guò)閾值時(shí)才會(huì)轉(zhuǎn)換為真正的跳躍表+哈希表實(shí)現(xiàn)。

  • (元素, score) 對(duì)順序存儲(chǔ)

數(shù)據(jù)一致性

Redis 通過(guò)保證所有寫操作(ZADD/ZREM等)都同時(shí)更新這兩個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)一致性。當(dāng)添加一個(gè)新成員時(shí):

  1. 會(huì)先將其添加到哈希表中
  2. 然后插入到跳躍表的正確位置

跳躍表(Skip List)詳解

基本概念

跳躍表是一種概率平衡的數(shù)據(jù)結(jié)構(gòu),它通過(guò)維護(hù)多級(jí)索引來(lái)提高有序鏈表的查找效率。它結(jié)合了鏈表和類似二分查找的特性。

數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)

Redis 中跳躍表的核心定義(簡(jiǎn)化版):

typedef struct zskiplistNode {
    robj *member;          // 成員對(duì)象(如字符串)
    double score;          // 分?jǐn)?shù)(用于排序)
    struct zskiplistNode *backward; // 后退指針(雙向鏈表)
    struct zskiplistLevel {
        struct zskiplistNode *forward; // 前進(jìn)指針
        unsigned int span;  // 跨度(用于計(jì)算排名)
    } level[];             // 柔性數(shù)組,表示節(jié)點(diǎn)的層級(jí)
} zskiplistNode;
 
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;  // 節(jié)點(diǎn)數(shù)量
    int level;            // 當(dāng)前最大層數(shù)
} zskiplist;

關(guān)鍵特性

層級(jí)隨機(jī)生成

  • 新節(jié)點(diǎn)的層數(shù)由隨機(jī)算法決定(冪次定律)
  • Redis 中最大層數(shù)為 32
int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

查找操作

  • 從最高層開(kāi)始查找
  • 如果當(dāng)前節(jié)點(diǎn)的值小于目標(biāo)值,則繼續(xù)前進(jìn)
  • 否則下降一層繼續(xù)查找
  • 時(shí)間復(fù)雜度:O(logN)

插入操作

  • 先查找插入位置
  • 隨機(jī)生成新節(jié)點(diǎn)的層數(shù)
  • 更新各層指針
  • 時(shí)間復(fù)雜度:O(logN)

刪除操作

  • 類似插入的逆過(guò)程
  • 時(shí)間復(fù)雜度:O(logN)

為什么選擇跳躍表?

Redis 選擇跳躍表而非平衡樹(如紅黑樹)的主要原因:

  • 實(shí)現(xiàn)簡(jiǎn)單:不需要復(fù)雜的旋轉(zhuǎn)操作
  • 范圍查詢高效:底層鏈表天然有序,便于范圍操作
  • 并發(fā)友好:更容易實(shí)現(xiàn)無(wú)鎖并發(fā)
  • 平均性能好:雖然最壞情況不如平衡樹,但實(shí)際表現(xiàn)優(yōu)異

ZSET中與哈希表的協(xié)作

當(dāng)執(zhí)行 ZADD 命令時(shí):

  • 先在哈希表中查找/更新 member-score 映射
  • 然后在跳躍表中插入/更新節(jié)點(diǎn)
  • 保證兩個(gè)操作的原子性

這種雙數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)使得 ZSET 能夠:

  • 快速判斷成員是否存在(哈希表)
  • 高效執(zhí)行范圍查詢(跳躍表)
  • 支持豐富的有序集合操作

Redis ZSET 實(shí)現(xiàn)滑動(dòng)時(shí)間窗口限流

Redis ZSET除了實(shí)現(xiàn)排行榜之類的排序功能,還能根據(jù)擁有排序的特性,簡(jiǎn)單的實(shí)現(xiàn)滑動(dòng)時(shí)間窗口限流功能。

關(guān)鍵步驟

  • 清理舊數(shù)據(jù)ZREMRANGEBYSCORE key -inf (currentTime - windowSize)
  • 統(tǒng)計(jì)當(dāng)前請(qǐng)求數(shù)ZCARD key
  • 檢查是否超限:比較當(dāng)前計(jì)數(shù)與閾值
  • 記錄新請(qǐng)求ZADD key currentTime uniqueId
  • 設(shè)置過(guò)期時(shí)間EXPIRE key windowSize + buffer

代碼實(shí)現(xiàn)(Spring Boot)

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
 
import java.util.Collections;
import java.util.UUID;
 
@Component
public class SlidingWindowLimiter {
    
    private final RedisTemplate<String, String> redisTemplate;
    
    // Lua腳本(原子操作)
    private static final String LUA_SCRIPT = 
        "local key = KEYS[1]\n" +
        "local now = tonumber(ARGV[1])\n" +
        "local window = tonumber(ARGV[2])\n" +
        "local maxRequests = tonumber(ARGV[3])\n" +
        "local requestId = ARGV[4]\n" +
        "\n" +
        "-- 1. 移除窗口外的舊數(shù)據(jù)\n" +
        "redis.call('ZREMRANGEBYSCORE', key, 0, now - window)\n" +
        "\n" +
        "-- 2. 獲取當(dāng)前請(qǐng)求數(shù)\n" +
        "local count = redis.call('ZCARD', key)\n" +
        "\n" +
        "-- 3. 檢查是否超限\n" +
        "if count >= maxRequests then\n" +
        "    return 0\n" +
        "end\n" +
        "\n" +
        "-- 4. 記錄本次請(qǐng)求\n" +
        "redis.call('ZADD', key, now, requestId)\n" +
        "\n" +
        "-- 5. 刷新過(guò)期時(shí)間\n" +
        "redis.call('EXPIRE', key, window/1000 + 10)\n" +
        "return 1";
 
    public SlidingWindowLimiter(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
 
    /**
     * 檢查請(qǐng)求是否允許通過(guò)
     * @param key 限流鍵(如 user_123:api_pay)
     * @param windowMillis 窗口大?。ê撩耄?
     * @param maxRequests 窗口內(nèi)允許的最大請(qǐng)求數(shù)
     * @return true=允許, false=限流
     */
    public boolean allowRequest(String key, long windowMillis, int maxRequests) {
        // 構(gòu)造Redis Key
        String redisKey = "rate_limit:" + key;
        
        // 準(zhǔn)備腳本參數(shù)
        long currentTime = System.currentTimeMillis();
        String requestId = UUID.randomUUID().toString();
        
        // 執(zhí)行Lua腳本
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(LUA_SCRIPT, Long.class);
        Long result = redisTemplate.execute(
            script,
            Collections.singletonList(redisKey),
            currentTime, windowMillis, maxRequests, requestId
        );
        
        return result != null && result == 1;
    }
}

使用示例

@RestController
public class PaymentController {
    
    @Autowired
    private SlidingWindowLimiter limiter;
    
    @PostMapping("/pay")
    public ResponseEntity<?> createPayment(@RequestBody PaymentRequest request) {
        // 構(gòu)造限流鍵: 用戶ID + 接口名
        String key = "user_" + request.getUserId() + ":payment";
        
        // 檢查限流: 每用戶每分鐘最多10次支付
        if (!limiter.allowRequest(key, 60000, 10)) {
            return ResponseEntity.status(429).body("請(qǐng)求過(guò)于頻繁");
        }
        
        // 執(zhí)行業(yè)務(wù)邏輯
        paymentService.process(request);
        return ResponseEntity.ok("支付成功");
    }
}

到此這篇關(guān)于Redis中ZSet數(shù)據(jù)結(jié)構(gòu)與滑動(dòng)窗口應(yīng)用的文章就介紹到這了,更多相關(guān)Redis ZSet滑動(dòng)窗口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis生成全局唯一ID的實(shí)現(xiàn)方法

    Redis生成全局唯一ID的實(shí)現(xiàn)方法

    全局唯一ID生成器是一種在分布式系統(tǒng)下用來(lái)生成全局唯一ID的工具,本文主要介紹了Redis生成全局唯一ID的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-06-06
  • redis使用zset實(shí)現(xiàn)延時(shí)隊(duì)列的示例代碼

    redis使用zset實(shí)現(xiàn)延時(shí)隊(duì)列的示例代碼

    本文主要介紹了redis使用zset實(shí)現(xiàn)延時(shí)隊(duì)列的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • redis實(shí)現(xiàn)延遲任務(wù)的項(xiàng)目實(shí)踐

    redis實(shí)現(xiàn)延遲任務(wù)的項(xiàng)目實(shí)踐

    本文主要介紹了redis實(shí)現(xiàn)延遲任務(wù)的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Redis客戶端及服務(wù)端的安裝教程詳解

    Redis客戶端及服務(wù)端的安裝教程詳解

    這篇文章主要介紹了Redis客戶端及服務(wù)端的安裝教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Redis Cluster集群數(shù)據(jù)分片機(jī)制原理

    Redis Cluster集群數(shù)據(jù)分片機(jī)制原理

    這篇文章主要介紹了Redis Cluster集群數(shù)據(jù)分片機(jī)制原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 詳解redis-cli?命令

    詳解redis-cli?命令

    這篇文章主要介紹了redis-cli?命令詳解,主要包括命令使用及使用info命令獲取服務(wù)器的信息,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • Reactor?WebFlux集成Redis處理緩存操作

    Reactor?WebFlux集成Redis處理緩存操作

    這篇文章主要為大家介紹了Reactor?WebFlux集成Redis處理緩存操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Redis動(dòng)態(tài)字符串SDS的實(shí)現(xiàn)

    Redis動(dòng)態(tài)字符串SDS的實(shí)現(xiàn)

    SDS在Redis中是實(shí)現(xiàn)字符串對(duì)象的工具,本文主要介紹了Redis動(dòng)態(tài)字符串SDS的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • Redis實(shí)現(xiàn)客戶端緩存的4種方式

    Redis實(shí)現(xiàn)客戶端緩存的4種方式

    客戶端緩存是指在應(yīng)用程序內(nèi)存中維護(hù)一份Redis數(shù)據(jù)的本地副本,以減少網(wǎng)絡(luò)請(qǐng)求次數(shù),降低延遲,并減輕Redis服務(wù)器負(fù)擔(dān),本文將分享Redis客戶端緩存的四種實(shí)現(xiàn)方式,大家可以參考一下
    2025-05-05
  • 攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問(wèn)題解決

    攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問(wèn)題解決

    本文主要介紹了攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評(píng)論

平度市| 南岸区| 仲巴县| 勃利县| 炎陵县| 灌阳县| 香港 | 施秉县| 桐乡市| 什邡市| 梓潼县| 那坡县| 湾仔区| 鹿泉市| 江都市| 锡林浩特市| 建阳市| 宁都县| 武义县| 徐水县| 香河县| 定边县| 旺苍县| 蒲江县| 电白县| 双桥区| 鹤岗市| 大荔县| 石棉县| 香格里拉县| 龙川县| 天门市| 连云港市| 巨鹿县| 大新县| 临安市| 靖江市| 延长县| 宝清县| 合川市| 屏东市|