Redis 有序集合 ZSet 深度解析教程

Redis-ZSet

    • 引言
    • 一、 ZSet 核心概念與特性
      • 1.1 什么是 ZSet?
      • 1.2 ZSet 與 Set、List 的本質區別
    • 二、 ZSet 典型應用場景
      • 2.1 排行榜 (Leaderboards)
      • 2.2 帶權重的任務隊列 / 延遲隊列
      • 2.3 時間軸 (Timeline)
      • 2.4 范圍查找
    • 三、 ZSet 底層實現
      • 3.1 ziplist (壓縮列表)
      • 3.2 skiplist (跳躍表) + dict (哈希表)
      • 3.3 編碼轉換
    • 四、 ZSet 常用命令詳解
      • 4.1 添加與更新
      • 4.2 刪除
      • 4.3 查詢
      • 4.4 集合運算 (交集與并集)
    • 五、 總結

引言

有序集合(Sorted Set,簡稱 ZSet)是一種非常強大且常用的數據結構。它既像集合(Set)一樣保證成員(member)的唯一性,又允許為每個成員關聯一個分數(score),并能根據分數對成員進行高效排序。

一、 ZSet 核心概念與特性

1.1 什么是 ZSet?

ZSet 是 Redis 提供的一種有序集合數據結構。你可以把它想象成一個集合,但這個集合里的每個元素(我們稱之為成員 member)都額外綁定了一個浮點數類型的分數 score。ZSet 最重要的特性就是它會根據 score 對 member 進行排序

核心特性總結:

  1. 成員唯一性 (Member Uniqueness): 和普通的 Set 一樣,ZSet 中的 member 是唯一的,不允許重復。如果你嘗試添加一個已經存在的 member,只會更新它的 score。
  2. 分數關聯 (Score Association): 每個 member 都必須關聯一個 score。這個 score 是一個浮點數,可以是正數、負數、零,甚至是 +inf (正無窮) 或 -inf (負無窮)。score 可以重復,即不同的 member 可以有相同的 score。
  3. 有序性 (Ordered): ZSet 內部的元素是根據 score 從小到大排序的。如果 score 相同,則按照 member 的字典序 (lexicographical order) 進行排序(具體取決于 Redis 版本和配置,但通常是這樣)。
  4. 高效操作: ZSet 提供了一系列高效的操作,比如添加/刪除成員、更新分數、根據分數范圍獲取成員、根據成員獲取排名/分數等。

1.2 ZSet 與 Set、List 的本質區別

為了更好地理解 ZSet,我們將其與 Redis 中另外兩種常用的集合類型 Set 和 List 進行比較:

特性ZSet (有序集合)Set (集合)List (列表)
存儲內容唯一的 member + 關聯的 score唯一的 membermember (可重復)
有序性根據 score 排序 (score 相同按字典序)無序按插入順序排序
成員唯一
核心優勢高效的排序、范圍查找、排名計算快速判斷成員是否存在、去重按順序存取、模擬棧/隊列
典型命令ZADD, ZRANGE, ZRANK, ZRANGEBYSCORESADD, SISMEMBER, SMEMBERSLPUSH, RPOP, LRANGE

總結來說:

  • 如果你只需要存儲唯一的值,不關心順序,用 Set
  • 如果你需要按照元素添加的順序存儲,并且允許重復,用 List
  • 如果你需要存儲唯一的元素,并且希望能夠根據某個權重(分數)進行排序和范圍查找,那么 ZSet 是不二之選。

二、 ZSet 典型應用場景

ZSet 的有序性和高效范圍查詢能力使其在眾多業務場景中大放異彩。以下是一些常見的應用示例:

2.1 排行榜 (Leaderboards)

這是 ZSet 最經典的應用場景之一。例如:

  • 游戲積分排行榜:member 是玩家 ID,score 是玩家積分。
  • 用戶貢獻排行榜:member 是用戶 ID,score 是貢獻值(如發帖數、獲贊數)。
  • 商品銷量排行榜:member 是商品 ID,score 是銷量或銷售額。

如何實現:

  1. 添加/更新用戶積分: 使用 ZADD 命令。如果用戶已存在,則更新其分數;如果不存在,則添加。
    # 添加或更新玩家 Alice 的積分為 1500
    ZADD game:leaderboard 1500 Alice
    # 添加或更新玩家 Bob 的積分為 2000
    ZADD game:leaderboard 2000 Bob
    # 使用 NX 選項,僅當 Bob 不存在時才添加 (這里不會成功,因為 Bob 已存在)
    ZADD game:leaderboard NX 2100 Bob
    # 使用 XX 選項,僅當 Alice 存在時才更新 (會成功)
    ZADD game:leaderboard XX 1600 Alice
    # 使用 INCR 選項,給 Alice 增加 100 分 (變為 1700)
    ZADD game:leaderboard INCR 100 Alice
    
  2. 獲取排名前 N 的用戶: 使用 ZREVRANGE (按分數從高到低排序)。
    # 獲取積分榜前 3 名的玩家及其分數
    ZREVRANGE game:leaderboard 0 2 WITHSCORES
    # 輸出可能類似:1) "Bob" 2) "2000" 3) "Alice" 4) "1700"
    
  3. 獲取用戶排名: 使用 ZREVRANK (從高到低排名,0 是第一名) 或 ZRANK (從低到高排名)。
    # 獲取 Alice 的排名 (從高到低)
    ZREVRANK game:leaderboard Alice
    # 輸出可能類似:1 (表示第二名,因為 Bob 是 0)
    
  4. 獲取用戶分數: 使用 ZSCORE
    # 獲取 Bob 的分數
    ZSCORE game:leaderboard Bob
    # 輸出: "2000"
    
  5. 獲取指定分數范圍內的用戶: 使用 ZRANGEBYSCOREZREVRANGEBYSCORE
    # 獲取分數在 1500 到 1800 之間的玩家 (包含邊界)
    ZRANGEBYSCORE game:leaderboard 1500 1800 WITHSCORES
    # 輸出可能類似:1) "Alice" 2) "1700"
    

2.2 帶權重的任務隊列 / 延遲隊列

有時我們需要處理一些帶有優先級的任務,或者需要在未來的某個特定時間點執行的任務。ZSet 可以很好地模擬這種場景。

  • member: 任務的唯一標識符 (如任務 ID)。
  • score:
    • 優先級: 分數越小(或越大,取決于業務定義)表示優先級越高。
    • 執行時間: 分數存儲任務應該被執行的 Unix 時間戳。

如何實現:

  1. 添加任務:
    # 添加一個優先級為 10 (數值越小優先級越高) 的任務 task:1
    ZADD priority_queue 10 task:1
    # 添加一個需要在未來時間戳 1678886400 執行的任務 task:2
    ZADD delayed_queue 1678886400 task:2
    
  2. 獲取優先級最高的任務: 使用 ZRANGE (取出分數最低的) 或 ZREVRANGE (取出分數最高的)。通常我們會結合 LIMIT 取出若干個。
    # 取出優先級最高的 1 個任務 (分數最低)
    ZRANGE priority_queue 0 0
    # 輸出: 1) "task:1"
    
  3. 獲取到期需要執行的任務: 使用 ZRANGEBYSCORE 獲取所有 score 小于等于當前時間戳的任務。
    # 假設當前時間戳是 1678886405
    # 取出所有 score 在 -inf 到 1678886405 之間的任務
    ZRANGEBYSCORE delayed_queue -inf 1678886405 WITHSCORES
    # 輸出可能類似: 1) "task:2" 2) "1678886400"
    
  4. 處理并移除任務: 獲取到任務后,需要使用 ZREM 將其從隊列中移除,防止重復處理。這是一個關鍵步驟,為了保證原子性(獲取并刪除),通常會結合 Lua 腳本來實現。
    -- Lua 腳本示例:原子地獲取并刪除優先級最高的任務
    local key = KEYS[1]
    local tasks = redis.call('ZRANGE', key, 0, 0) -- 獲取分數最低的任務
    if #tasks > 0 thenredis.call('ZREM', key, tasks[1]) -- 如果存在則刪除return tasks[1] -- 返回任務 ID
    elsereturn nil -- 沒有任務
    end
    
    或者使用 Redis 5.0+ 提供的阻塞彈出命令 BZPOPMINBZPOPMAX,它們能原子地完成“獲取并刪除”操作,并且在隊列為空時阻塞等待。
    # 阻塞等待,直到獲取并刪除 delayed_queue 中分數最低的任務,超時時間 60 秒
    BZPOPMIN delayed_queue 60
    # 輸出類似: 1) "delayed_queue" 2) "task:2" 3) "1678886400.0"
    

2.3 時間軸 (Timeline)

在社交應用中,用戶的 Feed 流(時間軸)通常需要展示關注的人發布的最新內容。

  • member: 內容的唯一 ID (如帖子 ID)。
  • score: 內容的發布時間戳。

如何實現:

  1. 發布內容時添加到關注者的 Timeline ZSet:
    # 用戶 UserA 發布了帖子 PostX (時間戳 1678887000)
    # 將 PostX 添加到關注者 Follower1 和 Follower2 的 Timeline ZSet
    ZADD timeline:Follower1 1678887000 PostX
    ZADD timeline:Follower2 1678887000 PostX
    
  2. 用戶拉取最新內容: 使用 ZREVRANGE 按時間戳倒序獲取最新的 N 條內容 ID。
    # Follower1 拉取最新的 10 條內容 ID
    ZREVRANGE timeline:Follower1 0 9
    
    拿到內容 ID 后,再根據 ID 去獲取內容的具體信息(通常存儲在 Hash 或 String 中)。

2.4 范圍查找

根據某個數值范圍進行查找,例如:

  • 查找價格在 100 到 200 元之間的商品。
  • 查找年齡在 18 到 25 歲之間的用戶。
  • 查找距離某個點特定范圍內的地點(結合 GeoHash)。

如何實現:

使用 ZRANGEBYSCOREZREVRANGEBYSCORE

# 假設有一個 ZSet 存儲商品價格: member 是商品 ID, score 是價格
ZADD product_prices 150 product:1 99.9 product:2 210 product:3 180 product:4# 查找價格在 100 到 200 (包含邊界) 之間的商品
ZRANGEBYSCORE product_prices 100 200 WITHSCORES
# 輸出: 1) "product:1" 2) "150" 3) "product:4" 4) "180"# 查找價格在 (100, 200] 之間的商品 (不包含 100)
ZRANGEBYSCORE product_prices (100 200 WITHSCORES

三、 ZSet 底層實現

為了實現高效的排序和查找,Redis ZSet 的底層實現會根據存儲數據的規模動態選擇不同的編碼方式。主要有兩種:ziplistskiplist + dict

3.1 ziplist (壓縮列表)

觸發條件:

當 ZSet 同時滿足以下兩個條件時,會優先采用 ziplist 編碼:

  1. ZSet 中元素的數量小于 zset_max_ziplist_entries 配置的值(默認 128)。
  2. ZSet 中每個元素(member 和 score)的字節長度都小于 zset_max_ziplist_value 配置的值(默認 64)。

結構與原理:

ziplist 是一種設計非常緊湊的連續內存數據結構,旨在盡可能地節省內存。它不像普通的數組那樣每個元素占用固定大小的空間,而是根據實際內容動態調整每個節點的長度。

一個 ziplist 的大致結構如下:

<zlbytes> <zltail> <zllen> <entry1> <entry2> ... <entryN> <zlend>
  • zlbytes: (4 字節) 記錄整個 ziplist 占用的總字節數。
  • zltail: (4 字節) 記錄到最后一個 entry 的偏移量,用于快速定位表尾。
  • zllen: (2 字節) 記錄 ziplist 中的 entry 數量。當數量超過 65535 時,該字段固定為 65535,需要遍歷才能確定實際數量。
  • entryX: 實際存儲數據的節點。每個 entry 包含前一個 entry 的長度信息(用于反向遍歷)和當前 entry 的編碼及內容。member 和 score 在 ziplist 中是緊鄰存儲的兩個 entry。
  • zlend: (1 字節) 特殊標記,值為 0xFF,表示 ziplist 的末尾。

entry 的結構(重點):

<prevrawlen> <encoding> <content>
  • prevrawlen: 記錄前一個 entry 的總長度。這個字段的長度本身是可變的(1字節或5字節),用于支持從后向前遍歷 ziplist。
  • encoding: 記錄當前 content 的編碼方式(字符串還是整數)以及長度。
  • content: 實際存儲的數據(member 或 score)。score 會被存儲為字符串形式。

ziplist 如何存儲 ZSet 元素:

在 ziplist 中,一個 ZSet 元素由兩個相鄰的 entry 表示:第一個 entry 存儲 member,第二個 entry 存儲 score。它們是成對出現的。

源碼片段 (ziplist.c 附近,概念性展示,非精確代碼):

// 概念性展示 ziplist entry 結構
typedef struct zlentry {unsigned int prevrawlensize; // 存儲前一個節點長度所需的字節數 (1 或 5)unsigned int prevrawlen;     // 前一個節點的長度unsigned int lensize;        // 存儲當前節點 content 長度或類型所需的字節數unsigned int len;            // 當前節點 content 的長度unsigned int headersize;     // 當前節點頭部 (prevrawlen + encoding) 的總大小unsigned char encoding;      // 編碼類型unsigned char *p;            // 指向當前節點數據的指針 (content)
} zlentry;// 在 ziplist 中查找元素大致需要遍歷比較
// 插入或刪除可能引起連鎖更新

優點:

  • 內存效率高: 連續存儲,沒有指針開銷(相比鏈表),節點長度可變,非常節省內存。

缺點:

  • 查找效率較低: 查找特定 member 或 score 需要遍歷 ziplist,時間復雜度為 O(N),N 是元素數量。范圍查找效率也不高。
  • 連鎖更新 (Cascade Update): 這是 ziplist 最大的問題。當插入或刪除一個 entry 時,如果導致后續 entry 的 prevrawlen 字段長度發生變化(比如從 1 字節變成 5 字節),就可能需要調整后續所有 entry 的位置,引發連鎖反應,導致操作的時間復雜度在最壞情況下變為 O(N^2)。更新操作也可能觸發。

3.2 skiplist (跳躍表) + dict (哈希表)

觸發條件:

當 ZSet 不再滿足 ziplist 的編碼條件時(即元素數量超過 zset_max_ziplist_entries 或任一元素的長度超過 zset_max_ziplist_value),Redis 會自動將其編碼轉換為 skiplist + dict注意:這個轉換是單向的,一旦變成 skiplist,即使后來元素減少,也不會自動轉回 ziplist。

結構與原理:

這種編碼方式結合了跳躍表 (skiplist) 和哈希表 (dict) 的優點:

  • dict (哈希表): 用于存儲從 memberscore 的映射。這使得通過 member 快速查找其對應的 score (如 ZSCORE 命令) 的平均時間復雜度達到 O(1)
  • skiplist (跳躍表): 用于存儲所有 ZSet 元素,并按照 score 進行排序。跳躍表是一種通過多層有序鏈表實現高效查找、插入、刪除的數據結構,其操作的平均時間復雜度為 O(log N),最壞情況下為 O(N)。它特別擅長進行范圍查找(如 ZRANGE, ZRANGEBYSCORE)。

為什么需要兩者結合?

  • 如果只用 dict,無法高效地按 score 排序和范圍查找。
  • 如果只用 skiplist,雖然也能通過 member 找到 score(遍歷 skiplist),但平均時間復雜度是 O(log N),不如 dict 的 O(1) 高效。ZSCORE 是常用操作,效率很重要。

兩者結合,可以在 O(1) 時間內通過 member 獲取 score,同時在 O(log N) 時間內完成基于 score 的排序、排名和范圍查找。

skiplist (跳躍表) 詳解 (重點和難點):

跳躍表是一種概率性數據結構,它在有序鏈表的基礎上增加了額外的“快速通道”(前向指針),從而實現類似二分查找的效率。

結構:

一個跳躍表包含:

  • header: 頭節點,不存儲實際數據,但包含指向各層鏈表頭部的指針。
  • tail: 指向跳躍表尾節點的指針。
  • length: 跳躍表中節點的數量。
  • level: 跳躍表中當前最高的層數。

每個跳躍表節點 (zskiplistNode) 包含:

  • member (ele): ZSet 的成員。
  • score: ZSet 的分數。
  • backward: 指向前一個節點的指針(用于反向遍歷,如 ZREVRANGE)。
  • level[]: 一個柔性數組(flexible array member),存儲該節點在每一層前向指針 (forward)跨度 (span)
    • forward: 指向該層鏈表中下一個節點的指針。
    • span: 表示當前節點的 forward 指針指向的節點與當前節點之間跨越了多少個節點。span 對于快速計算排名 (ZRANK) 至關重要。

圖解 skiplist:

          header                                                     tail
level 3:   head ->-------------------------------------------------> NULL
level 2:   head ->------------------------> node E -----------------> NULL
level 1:   head ->--------> node B ------> node E ->-----> node G --> NULL
level 0:   head --> node A -> node B -> node C -> node E -> node F -> node G -> NULL
(score)            (10)      (20)      (30)      (50)      (60)      (70)(span)
level 3:            span=7
level 2:            span=4                   span=3
level 1:            span=2        span=2               span=2
level 0:            span=1        span=1     span=1    span=1     span=1     span=1
  • 第 0 層包含所有節點,是一個標準的有序鏈表。
  • 更高層級的鏈表是第 0 層的“快速通道”,它們只包含部分節點。
  • 一個節點會出現在多少層(除了第 0 層)是隨機決定的,但層數越高的概率越低(通常是 P=1/4 或 1/2)。這種隨機性保證了跳躍表在插入、刪除、查找操作上的平均時間復雜度為 O(log N)。
  • span 表示從當前節點沿著該層的 forward 指針跳到下一個節點,中間跳過了多少個底層節點。例如,在 level 2,header 的 forward 指向 node E,span 為 4,表示從 header 到 node E 之間有 4 個節點 (A, B, C, E)。

查找過程 (例如查找 score=50 的節點 E):

  1. 從最高層 (level 3) 的 header 開始。forward 指向 NULL,比 50 大。
  2. 下降到 level 2。header 的 forward 指向 node E (score 50)。找到目標。

查找過程 (例如查找 score=65 的節點,它不存在):

  1. 從最高層 (level 3) 的 header 開始。forward 指向 NULL,比 65 大。
  2. 下降到 level 2。header 的 forward 指向 node E (50),比 65 小。沿著 level 2 的 forward 到達 node E。node E 在 level 2 的 forward 指向 NULL,比 65 大。
  3. 下降到 level 1。node E 的 forward 指向 node G (70),比 65 大。
  4. 下降到 level 0。node E 的 forward 指向 node F (60),比 65 小。沿著 level 0 的 forward 到達 node F。node F 的 forward 指向 node G (70),比 65 大。
  5. 此時,我們位于 node F,并且下一節點 score (70) 大于目標 (65)。查找結束,目標不存在。插入位置應該在 F 和 G 之間。

計算排名 (ZRANK,例如計算 node E 的排名):

  1. 從最高層開始查找,累加跨過的 span 值。
  2. Level 3: header -> NULL。不前進。rank=0。
  3. Level 2: header -> node E。前進了,累加 header 在 level 2 的 span (假設為 4)。rank = 4。找到目標。
  4. 排名是 rank - 1 (因為排名從 0 開始),所以 node E 的排名是 3。

源碼片段 (server.h, t_zset.c 附近,結構定義和關鍵操作):

/* server.h */
#define ZSKIPLIST_MAXLEVEL 32 /* 跳躍表最大層數 */
#define ZSKIPLIST_P 0.25      /* 用于計算隨機層數的概率 *//* 跳躍表節點 */
typedef struct zskiplistNode {sds ele; // 成員 (member)double score; // 分數struct zskiplistNode *backward; // 后向指針// 層級數組,包含前向指針和跨度struct zskiplistLevel {struct zskiplistNode *forward; // 前向指針unsigned long span; // 跨度} level[]; // 柔性數組
} zskiplistNode;/* 跳躍表 */
typedef struct zskiplist {struct zskiplistNode *header, *tail; // 頭尾節點指針unsigned long length; // 節點數量int level; // 當前最大層數
} zskiplist;/* ZSet 結構 */
typedef struct zset {dict *dict; // 哈希表,member -> scorezskiplist *zsl; // 跳躍表,按 score 排序
} zset;/* t_zset.c - zslInsert: 插入新節點到跳躍表 */
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {zskiplistNode *update[ZSKIPLIST_MAXLEVEL]; // 記錄每層查找路徑上需要更新 forward 指針的節點unsigned int rank[ZSKIPLIST_MAXLEVEL]; // 記錄每層查找到的位置的排名 (用于計算 span)zskiplistNode *x;int i, level;// 1. 查找插入位置,并記錄路徑 (update 數組) 和排名 (rank 數組)x = zsl->header;for (i = zsl->level-1; i >= 0; i--) {rank[i] = (i == zsl->level-1) ? 0 : rank[i+1]; // 初始化排名// 在當前層向右查找,直到找到第一個 score 更大或 ele 字典序更大的節點while (x->level[i].forward &&(x->level[i].forward->score < score ||(x->level[i].forward->score == score &&sdscmp(x->level[i].forward->ele,ele) < 0))) // 如果分數相同,比較成員字典序{rank[i] += x->level[i].span; // 累加跨度到排名x = x->level[i].forward; // 前進}update[i] = x; // 記錄該層需要更新 forward 指針的節點}// 2. 計算新節點的隨機層數level = zslRandomLevel(); // 根據概率 P 計算一個隨機層數// 3. 如果新層數高于當前跳躍表最大層數,初始化 update 和 rank 數組中新層的數據if (level > zsl->level) {for (i = zsl->level; i < level; i++) {rank[i] = 0;update[i] = zsl->header;update[i]->level[i].span = zsl->length; // 新層的 header span 為當前總長度}zsl->level = level; // 更新跳躍表最大層數}// 4. 創建新節點x = zslCreateNode(level,score,ele); // 分配內存并初始化新節點// 5. 更新每一層的 forward 指針和 spanfor (i = 0; i < level; i++) {// 將新節點插入到 update[i] 和原 update[i]->level[i].forward 之間x->level[i].forward = update[i]->level[i].forward;update[i]->level[i].forward = x;// 更新 span 值// 新節點的 span = 原 update[i] 的 span - (新節點之前經過的節點數 rank[0] - update[i] 之前的節點數 rank[i])x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);// update[i] 的 span = (新節點之前經過的節點數 rank[0] - update[i] 之前的節點數 rank[i]) + 1 (新節點本身)update[i]->level[i].span = (rank[0] - rank[i]) + 1;}// 6. 如果新節點的層數低于原跳躍表最大層數,更新未涉及層級的 span (加 1,因為多了一個節點)for (i = level; i < zsl->level; i++) {update[i]->level[i].span++;}// 7. 更新新節點的 backward 指針x->backward = (update[0] == zsl->header) ? NULL : update[0];if (x->level[0].forward)x->level[0].forward->backward = x; // 更新后繼節點的 backward 指針elsezsl->tail = x; // 如果新節點是最后一個節點,更新 tail 指針// 8. 更新跳躍表長度zsl->length++;return x;
}

優點:

  • 高效的查找、插入、刪除: 平均時間復雜度為 O(log N)。
  • 高效的范圍查詢: 按 score 范圍查找非常快。
  • 高效的排名計算: 利用 span 可以在 O(log N) 內計算排名。

缺點:

  • 內存開銷相對較大: 相較于 ziplist,需要存儲額外的指針 (forward, backward) 和 span 信息,內存占用更多。

3.3 編碼轉換

  • ziplist -> skiplist: 當 ZSet 不再滿足 ziplist 的兩個條件(元素數量或大小超限)時,Redis 會自動執行轉換。這個過程需要創建新的 dictskiplist,并將 ziplist 中的所有元素逐一添加到新的數據結構中。這是一個一次性的、相對耗時的操作,會消耗額外的 CPU 和內存。但轉換完成后,后續操作將受益于 skiplist 的高效率。
  • skiplist -> ziplist: Redis 不會自動執行此轉換。即使 ZSet 的元素數量和大小降回 ziplist 的閾值以下,它仍然會保持 skiplist 編碼。

你可以使用 OBJECT ENCODING key 命令查看一個 ZSet 當前使用的底層編碼。


四、 ZSet 常用命令詳解

下面我們詳細介紹 ZSet 的常用命令,包括其功能、參數、返回值、時間復雜度和示例。時間復雜度會根據底層編碼(ziplist 或 skiplist)有所不同。

復雜度說明:

  • N: ZSet 中的元素數量。
  • M: 被操作的元素數量。
  • LogN: 通常指 log base 2 of N。

4.1 添加與更新

  • ZADD key [NX|XX] [CH] [INCR] score member [score member ...]

    • 功能: 向有序集合添加一個或多個成員,或者更新已存在成員的分數。
    • 參數:
      • NX: 僅當成員不存在時才添加。不更新已存在的成員。
      • XX: 僅當成員存在時才更新。不添加新成員。
      • CH: (Changed) 返回值從“新添加成員的數量”變為“被修改成員的總數”(包括添加和更新)。
      • INCR: 將命令模式從“添加/更新”變為“增加分數”。此時只能指定一對 score memberscore 表示要增加的值(可以是負數)。如果 member 不存在,則添加它,score 為指定的增量值。
    • 返回值: 默認情況下,返回新添加到集合中的成員數量(不包括分數被更新的成員)。如果使用了 CH 選項,返回被修改(添加或更新)的成員總數。如果使用了 INCR 選項,返回成員的新分數(字符串形式)。
    • 復雜度:
      • 添加單個元素:O(log N) (skiplist) / 平均 O(log N),最壞 O(N^2) 因連鎖更新 (ziplist)
      • 添加多個元素:O(M * log N) (skiplist) / 平均 O(M * log N),最壞 O(N*M) 或 O(N^2) (ziplist)
    • 示例: (見應用場景部分)
  • ZINCRBY key increment member

    • 功能: 為有序集合中指定成員的分數增加 increment。如果成員不存在,則添加它,分數等于 increment。相當于 ZADD key INCR increment member
    • 參數:
      • increment: 要增加的分數值(浮點數)。
      • member: 要操作的成員。
    • 返回值: 成員的新分數(字符串形式)。
    • 復雜度: O(log N) (skiplist) / 平均 O(log N),最壞 O(N^2) (ziplist)
    • 示例:
      ZADD scores 10 user1
      ZINCRBY scores 5 user1  # user1 的分數變為 15
      # 返回: "15"
      ZINCRBY scores 3 user_new # user_new 不存在,添加,分數為 3
      # 返回: "3"
      

4.2 刪除

  • ZREM key member [member ...]

    • 功能: 移除有序集合中的一個或多個成員。忽略不存在的成員。
    • 返回值: 被成功移除的成員數量。
    • 復雜度:
      • 移除單個元素:O(log N) (skiplist) / 平均 O(log N),最壞 O(N^2) (ziplist)
      • 移除多個元素:O(M * log N) (skiplist) / 平均 O(M * log N),最壞 O(N*M) 或 O(N^2) (ziplist)
    • 示例:
      ZADD myzset 1 one 2 two 3 three
      ZREM myzset one four # 移除 one 和 four (four 不存在)
      # 返回: 1
      
  • ZREMRANGEBYRANK key start stop

    • 功能: 移除有序集合中指定排名范圍內的所有成員。排名按分數從小到大計算,0 是第一個,-1 是最后一個。
    • 參數:
      • start, stop: 排名范圍(包含邊界)。
    • 返回值: 被移除成員的數量。
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist),M 是被移除的數量。
    • 示例:
      ZADD myzset 1 a 2 b 3 c 4 d 5 e
      # 移除排名 0 到 1 的成員 (a, b)
      ZREMRANGEBYRANK myzset 0 1
      # 返回: 2
      # 剩余: c, d, e
      # 移除排名最后 2 位的成員 (d, e)
      ZREMRANGEBYRANK myzset -2 -1
      # 返回: 2
      # 剩余: c
      
  • ZREMRANGEBYSCORE key min max

    • 功能: 移除有序集合中指定分數范圍內的所有成員。
    • 參數:
      • min, max: 分數范圍。默認包含邊界。可以使用 ( 開頭表示不包含最小值/最大值,如 (10 20 表示 > 10 且 <= 20。可以使用 -inf+inf 表示負無窮和正無窮。
    • 返回值: 被移除成員的數量。
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist),M 是被移除的數量。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d 50 e
      # 移除分數在 [20, 40] 之間的成員 (b, c, d)
      ZREMRANGEBYSCORE myzset 20 40
      # 返回: 3
      # 剩余: a, e
      # 移除分數 > 45 的成員 (e)
      ZREMRANGEBYSCORE myzset (45 +inf
      # 返回: 1
      # 剩余: a
      

4.3 查詢

  • ZCARD key

    • 功能: 獲取有序集合的成員數量(基數)。
    • 返回值: 成員數量。
    • 復雜度: O(1)。無論哪種編碼,長度信息都是直接可用的。
    • 示例:
      ZADD myzset 1 a 2 b
      ZCARD myzset
      # 返回: 2
      
  • ZSCORE key member

    • 功能: 獲取指定成員的分數。
    • 返回值: 成員的分數(字符串形式)。如果成員不存在,返回 nil
    • 復雜度: O(1) (skiplist,通過 dict) / O(N) (ziplist,需要遍歷)。
    • 示例:
      ZADD myzset 1 a
      ZSCORE myzset a
      # 返回: "1"
      ZSCORE myzset non_exist
      # 返回: nil
      
  • ZRANK key member

    • 功能: 獲取指定成員的排名(按分數從小到大排序)。排名從 0 開始。
    • 返回值: 成員的排名(整數)。如果成員不存在,返回 nil
    • 復雜度: O(log N) (skiplist,利用 span) / O(N) (ziplist,需要遍歷)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c
      ZRANK myzset b
      # 返回: 1 (因為 a 是 0, b 是 1)
      
  • ZREVRANK key member

    • 功能: 獲取指定成員的排名(按分數從高到低排序)。排名從 0 開始。
    • 返回值: 成員的排名(整數)。如果成員不存在,返回 nil
    • 復雜度: O(log N) (skiplist) / O(N) (ziplist)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c
      ZREVRANK myzset b
      # 返回: 1 (因為 c 是 0, b 是 1)
      
  • ZCOUNT key min max

    • 功能: 獲取有序集合中,分數在指定范圍內的成員數量。
    • 參數:
      • min, max: 分數范圍,語法同 ZREMRANGEBYSCORE
    • 返回值: 指定分數范圍內的成員數量。
    • 復雜度: O(log N) (skiplist,定位到范圍起點即可) / O(N) (ziplist)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d 50 e
      ZCOUNT myzset 20 40
      # 返回: 3 (b, c, d)
      ZCOUNT myzset (20 +inf
      # 返回: 3 (c, d, e)
      
  • ZRANGE key start stop [WITHSCORES]

    • 功能: 獲取有序集合中指定排名范圍內的成員(按分數從小到大排序)。
    • 參數:
      • start, stop: 排名范圍(包含邊界),0 是第一個,-1 是最后一個,-2 是倒數第二個,以此類推。
      • WITHSCORES: 可選,同時返回成員的分數。
    • 返回值: 成員列表。如果使用 WITHSCORES,則返回 [member1, score1, member2, score2, ...] 格式的列表。
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist),M 是返回的數量。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d
      # 獲取排名 0 到 1 的成員 (a, b)
      ZRANGE myzset 0 1
      # 返回: 1) "a" 2) "b"
      # 獲取排名 1 到 -1 (最后一個) 的成員及其分數 (b, c, d)
      ZRANGE myzset 1 -1 WITHSCORES
      # 返回: 1) "b" 2) "20" 3) "c" 4) "30" 5) "d" 6) "40"
      
  • ZREVRANGE key start stop [WITHSCORES]

    • 功能: 獲取有序集合中指定排名范圍內的成員(按分數從高到低排序)。
    • 參數:ZRANGE
    • 返回值:ZRANGE
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d
      # 獲取排名 0 到 1 的成員 (按分數從高到低,即 d, c)
      ZREVRANGE myzset 0 1
      # 返回: 1) "d" 2) "c"
      # 獲取所有成員及其分數 (按分數從高到低)
      ZREVRANGE myzset 0 -1 WITHSCORES
      # 返回: 1) "d" 2) "40" 3) "c" 4) "30" 5) "b" 6) "20" 7) "a" 8) "10"
      
  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

    • 功能: 獲取有序集合中,分數在指定范圍內的成員(按分數從小到大排序)。
    • 參數:
      • min, max: 分數范圍,語法同 ZREMRANGEBYSCORE
      • WITHSCORES: 可選,同時返回成員的分數。
      • LIMIT offset count: 可選,用于分頁。從符合條件的成員中,跳過 offset 個,取出 count 個。
    • 返回值: 成員列表(可能包含分數)。
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist),M 是返回的數量(在 LIMIT 前計算)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d 50 e
      # 獲取分數在 [20, 40] 之間的成員及其分數 (b, c, d)
      ZRANGEBYSCORE myzset 20 40 WITHSCORES
      # 返回: 1) "b" 2) "20" 3) "c" 4) "30" 5) "d" 6) "40"
      # 獲取分數 > 25 的成員,跳過 1 個,取 2 個 (d, e)
      ZRANGEBYSCORE myzset (25 +inf WITHSCORES LIMIT 1 2
      # 返回: 1) "d" 2) "40" 3) "e" 4) "50"
      
  • ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

    • 功能: 獲取有序集合中,分數在指定范圍內的成員(按分數從高到低排序)。注意 maxmin 的順序。
    • 參數:ZRANGEBYSCORE,但 max 在前,min 在后。
    • 返回值: 成員列表(可能包含分數)。
    • 復雜度: O(log N + M) (skiplist) / O(N) (ziplist)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c 40 d 50 e
      # 獲取分數在 [40, 20] 之間的成員及其分數 (按分數從高到低,即 d, c, b)
      ZREVRANGEBYSCORE myzset 40 20 WITHSCORES
      # 返回: 1) "d" 2) "40" 3) "c" 4) "30" 5) "b" 6) "20"
      # 獲取分數 <= 35 的成員,按分數從高到低,取前 2 個 (c, b)
      ZREVRANGEBYSCORE myzset 35 -inf WITHSCORES LIMIT 0 2
      # 返回: 1) "c" 2) "30" 3) "b" 4) "20"
      
  • ZPOPMIN key [count] (Redis 5.0+)

    • 功能: 移除并返回有序集合中分數最低的一個或多個成員。
    • 參數:
      • count: 可選,指定要移除并返回的成員數量,默認為 1。
    • 返回值: 被移除的成員及其分數列表 [member1, score1, member2, score2, ...]。如果集合為空,返回空列表。
    • 復雜度: O(log N * M),M 是 count 值。
    • 示例:
      ZADD myzset 10 a 20 b 30 c
      ZPOPMIN myzset 2 # 移除并返回 a 和 b
      # 返回: 1) "a" 2) "10" 3) "b" 4) "20"
      # 集合剩余: c
      
  • ZPOPMAX key [count] (Redis 5.0+)

    • 功能: 移除并返回有序集合中分數最高的一個或多個成員。
    • 參數:ZPOPMIN
    • 返回值:ZPOPMIN
    • 復雜度: O(log N * M)。
    • 示例:
      ZADD myzset 10 a 20 b 30 c
      ZPOPMAX myzset 1 # 移除并返回 c
      # 返回: 1) "c" 2) "30"
      # 集合剩余: a, b
      
  • BZPOPMIN key [key ...] timeout (Redis 5.0+)

    • 功能: ZPOPMIN 的阻塞版本。如果所有指定的 ZSet 都為空,連接將阻塞 timeout 秒,直到有元素可彈出或超時。timeout 為 0 表示無限期阻塞。它會從第一個非空 ZSet 中彈出元素。
    • 返回值: 一個包含 3 個元素的列表:彈出元素的來源 ZSet 的鍵名、被彈出的成員、成員的分數。如果超時,返回 nil
    • 復雜度: O(log N)。
    • 示例: (用于實現可靠的任務隊列)
      # 阻塞等待從 queue1 或 queue2 中彈出分數最低的元素,最多等 10 秒
      BZPOPMIN queue1 queue2 10
      
  • BZPOPMAX key [key ...] timeout (Redis 5.0+)

    • 功能: ZPOPMAX 的阻塞版本。
    • 返回值:BZPOPMIN
    • 復雜度: O(log N)。
  • ZSCAN key cursor [MATCH pattern] [COUNT count]

    • 功能: 迭代有序集合中的元素(成員和分數)。用于遍歷大型 ZSet 而不阻塞服務器。
    • 參數:
      • cursor: 游標,第一次迭代從 0 開始,后續迭代使用上次返回的游標。
      • MATCH pattern: 可選,只返回匹配給定模式的成員。
      • COUNT count: 可選,提示每次迭代返回的元素數量(不保證精確)。
    • 返回值: 一個包含兩個元素的列表:第一個是下一次迭代使用的 cursor(如果返回 “0” 表示迭代完成),第二個是本次迭代返回的元素列表 [member1, score1, member2, score2, ...]
    • 復雜度: 每次調用 O(M),M 是 COUNT 值。完整遍歷需要 O(N)。
    • 示例:
      # 第一次迭代
      ZSCAN myzset 0 MATCH user:* COUNT 10
      # 返回: 1) "17"  (下次的 cursor)
      #      2) 1) "user:1" 2) "100" 3) "user:3" 4) "150" ... (最多 10 對)# 后續迭代
      ZSCAN myzset 17 MATCH user:* COUNT 10
      

4.4 集合運算 (交集與并集)

  • ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

    • 功能: 計算一個或多個有序集合的交集,并將結果存儲在 destination 鍵中。對于交集中的成員,其分數可以按指定方式聚合。
    • 參數:
      • destination: 存儲結果的鍵名。如果已存在,會被覆蓋。
      • numkeys: 要計算交集的 ZSet 數量。
      • key [key ...]: 要計算交集的 ZSet 的鍵名。
      • WEIGHTS weight [weight ...]: 可選,為每個輸入 ZSet 指定一個權重因子,計算交集成員分數時會先乘以權重。默認權重為 1。
      • AGGREGATE SUM|MIN|MAX: 可選,指定如何聚合交集成員的分數。SUM (默認): 各 ZSet 中分數之和 (乘以權重后)。MIN: 取最小值。MAX: 取最大值。
    • 返回值: 存儲在 destination 中的結果集合的成員數量。
    • 復雜度: O(NKLogM) 最壞情況,N 是最小輸入 ZSet 的大小,K 是輸入 ZSet 的數量,M 是結果 ZSet 的大小。通常取決于最小 ZSet 的大小。
    • 示例:
      ZADD zset1 1 one 2 two
      ZADD zset2 1 one 2 two 3 three
      # 計算 zset1 和 zset2 的交集,分數相加,存入 zset_inter
      ZINTERSTORE zset_inter 2 zset1 zset2 WEIGHTS 1 1 AGGREGATE SUM
      # 返回: 2 (交集有 one, two)
      ZRANGE zset_inter 0 -1 WITHSCORES
      # 返回: 1) "one" 2) "2" (1*1 + 1*1) 3) "two" 4) "4" (2*1 + 2*1)ZADD zset3 5 one 1 two 10 four
      # 計算 zset1 和 zset3 交集,zset1 權重 2,zset3 權重 3,取最大分數
      ZINTERSTORE zset_inter2 2 zset1 zset3 WEIGHTS 2 3 AGGREGATE MAX
      # 返回: 2 (交集有 one, two)
      ZRANGE zset_inter2 0 -1 WITHSCORES
      # 返回: 1) "one" 2) "15" (max(1*2, 5*3)) 2) "two" 4) "4" (max(2*2, 1*3))
      
  • ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

    • 功能: 計算一個或多個有序集合的并集,并將結果存儲在 destination 鍵中。
    • 參數:ZINTERSTORE
    • 返回值: 存儲在 destination 中的結果集合的成員數量。
    • 復雜度: O(NLogN + MLogM),N 是所有輸入 ZSet 成員總數,M 是結果 ZSet 大小。通常取決于所有輸入 ZSet 的總大小。
    • 示例:
      ZADD zset1 1 one 2 two
      ZADD zset2 1 one 2 two 3 three
      # 計算 zset1 和 zset2 的并集,分數相加,存入 zset_union
      ZUNIONSTORE zset_union 2 zset1 zset2 AGGREGATE SUM
      # 返回: 3 (并集有 one, two, three)
      ZRANGE zset_union 0 -1 WITHSCORES
      # 返回: 1) "one" 2) "2" (1+1) 3) "three" 4) "3" 5) "two" 6) "4" (2+2)
      

五、 總結

Redis ZSet 是一種功能強大的有序集合數據結構,它通過將唯一的成員與浮點數分數相關聯,并根據分數進行排序,為許多業務場景提供了高效的解決方案。

  • 核心優勢: 成員唯一、按 score 排序、高效的排名計算和范圍查找。
  • 應用廣泛: 排行榜、延遲隊列、時間軸、地理位置搜索(底層)、范圍查詢等。
  • 底層實現: 根據數據規模動態選擇 ziplist(節省內存,小規模數據)或 skiplist + dict(查詢效率高,大規模數據)。skiplist 通過多層鏈表和 span 實現了 O(log N) 的高效操作。
  • 命令豐富: 提供了 ZADD, ZREM, ZRANGE, ZRANK, ZRANGEBYSCORE 等一系列覆蓋增刪改查、范圍查詢、集合運算的命令。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/77018.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77018.shtml
英文地址,請注明出處:http://en.pswp.cn/web/77018.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【SpringBoot】HttpServletRequest獲取使用及失效問題(包含@Async異步執行方案)

目錄 1. 在 Controller 方法中作為參數注入 2.使用 RequestContextHolder &#xff08;1&#xff09;失效問題 &#xff08;2&#xff09;解決方案一&#xff1a; &#xff08;3&#xff09;解決方案二&#xff1a; 3、使用AutoWrite自動注入HttpServletRequest 跨線程調…

mfc學習(一)

mfc為微軟創建的一個類qt框架的客戶端程序&#xff0c;只不過因為微軟目前有自己 的親身兒子C#&#xff08;.net&#xff09;,所以到2010沒有進行維護。然后一些的工業企業還在繼續進行維護相關的內容。我目前就接手一個現在這樣的項目&#xff0c;其實本質與qt的思路是差不多的…

HarmonyOS:一多能力介紹:一次開發,多端部署

概述 如果一個應用需要在多個設備上提供同樣的內容&#xff0c;則需要適配不同的屏幕尺寸和硬件&#xff0c;開發成本較高。HarmonyOS 系統面向多終端提供了“一次開發&#xff0c;多端部署”&#xff08;后文中簡稱為“一多”&#xff09;的能力&#xff0c;可以基于一種設計…

秒出PPT推出更強版本,AI PPT工具進入新紀元!

在現代職場中&#xff0c;PPT是我們溝通和展示信息的重要工具。無論是做產品演示&#xff0c;還是準備工作匯報&#xff0c;一份精美的PPT能大大提升演示效果。然而&#xff0c;傳統的PPT制作往往需要消耗大量時間&#xff0c;尤其是在排版、設計和內容調整上。如今&#xff0c…

Godot開發2D冒險游戲——第二節:主角光環整起來!

變量的作用域 全局變量&#xff0c;局部變量&#xff0c;導出變量&#xff08;可以在檢查器當中快速查看&#xff09; 為玩家添加移動動畫 現在游戲的玩家還只是在滑行&#xff0c;我們需要再添加玩家每個方向上的移動效果 刪除原先的Item節點&#xff0c;創建一個動畫精靈…

顛覆傳統NAS體驗:耘想WinNAS讓遠程存儲如同本地般便捷

在當今數據爆炸的時代&#xff0c;網絡附加存儲(NAS)已成為許多企業和個人用戶的必備設備。然而&#xff0c;傳統硬件NAS解決方案存在諸多限制&#xff0c;如高額成本、復雜設置和有限的遠程訪問能力。耘想WinNAS以其創新的軟件解決方案&#xff0c;徹底改變了這一局面&#xf…

新市場環境下新能源汽車電流傳感技術發展前瞻

新能源革命重構產業格局 在全球碳中和戰略驅動下&#xff0c;新能源汽車產業正經歷結構性變革。國際清潔交通委員會&#xff08;ICCT&#xff09;最新報告顯示&#xff0c;2023年全球新能源汽車滲透率突破18%&#xff0c;中國市場以42%的市占率持續領跑。這種產業變革正沿著&q…

STM32之DHT11溫濕度傳感器---附代碼

DHT11簡介 DHT11的供電電壓為 3&#xff0d;5.5V。 傳感器上電后&#xff0c;要等待 1s 以越過不穩定狀態在此期間無需發送任何指令。 電源引腳&#xff08;VDD&#xff0c;GND&#xff09;之間可增加一個100nF 的電容&#xff0c;用以去耦濾波。 DATA 用于微處理器與DHT11之間…

#define STEUER_A_H {PWM_A_ON}

目錄 一、括號的區別 二、實例講解 三、注意事項 四、總結 五、補充 一、括號的區別 大括號 {}: 在 C/C 中&#xff0c;大括號一般用于表示一個代碼塊或結構體、集合等。例如&#xff1a; 用于定義函數體、控制結構&#xff08;如 if、for&#xff09;的代碼塊。用于初始化…

Redis 緩存—處理高并發問題

Redis的布隆過濾器、單線程架構、雙寫一致性、比較穿透、擊穿及雪崩、緩存更新方案及分布式鎖。 1 布隆過濾器 是一種高效的概率型數據結構&#xff0c;用于判斷元素是否存在。主要用于防止緩存穿透&#xff0c;通過攔截不存在的數據查詢&#xff0c;避免擊穿數據庫。 原理&…

【玩轉全棧】—— 無敵前端究極動態組件庫--Inspira UI

目錄 Inspira UI 介紹 配置環境 使用示例 效果&#xff1a; Inspira UI 學習視頻&#xff1a; 華麗優雅 | Inspira UI快速上手_嗶哩嗶哩_bilibili 官網&#xff1a;https://inspira-ui.com/ Inspira UI 介紹 Inspira UI 是一個設計精美、功能豐富的用戶界面庫&#xff0c;專為…

【OpenCV圖像處理實戰】從基礎操作到工業級應用

目錄 前言技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解核心作用講解關鍵技術模塊說明技術選型對比 二、實戰演示環境配置要求核心代碼實現&#xff08;6個案例&#xff09;案例1&#xff1a;圖像基本操作案例2&#xff1a;邊緣檢測案例3&…

fastjson使用parseObject轉換成JSONObject出現將字符特殊字符解析解決

現象&#xff1a;將字符串的${TARGET_VALUE}轉換成NULL字符串了問題代碼&#xff1a; import com.alibaba.fastjson.JSON;JSONObject config JSON.parseObject(o.toString()); 解決方法&#xff1a; 1.更換fastjson版本 import com.alibaba.fastjson2.JSON;或者使用其他JS…

Docker Compose 和 Kubernetes(k8s)區別

前言&#xff1a;Docker Compose 和 Kubernetes&#xff08;k8s&#xff09;是容器化技術中兩個常用的工具&#xff0c;但它們的定位、功能和適用場景有顯著區別。以下是兩者的核心對比&#xff1a; ??1. 定位與目標?? ??特性?? ??Docker Compose?? ??Kubernet…

【21天學習打卡挑戰賽】如何學習WEB安全:逼自己在短時間掌握WEB安全核心內容

&#x1f36c; 博主介紹 &#x1f468;?&#x1f393; 博主介紹&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高興認識大家~ ?主攻領域&#xff1a;【滲透領域】【數據通信】 【通訊安全】 【web安全】【面試分析】 &#x1f389;點贊?評論?收藏 養成習…

Oracle數據庫巡檢腳本

1.查詢實例信息 SELECT INST_ID, INSTANCE_NAME, TO_CHAR(STARTUP_TIME, YYYY-MM-DD HH24:MI:SS) AS STARTUP_TIME FROM GV$INSTANCE ORDER BY INST_ID; 2.查看是否歸檔 archive log list 3.查看數據庫參數 SELECT NAME , TYPE , VALUE FROM V$PARAMETER ORDER BY NAME; 4.…

Windows 安裝 JDK

下載 Java8 的下載直接訪問&#xff1a;https://www.oracle.com/java/technologies/downloads/#java8-windows https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html 接受協議后點擊下載&#xff0c;再輸入賬號信息就可以下載了。 如果…

強化學習核心原理及數學框架

1. 定義與核心思想 強化學習&#xff08;Reinforcement Learning, RL&#xff09;是一種通過智能體&#xff08;Agent&#xff09;與環境&#xff08;Environment&#xff09;的持續交互來學習最優決策策略的機器學習范式。其核心特征為&#xff1a; ??試錯學習??&#x…

Netty前置基礎知識之BIO、NIO以及AIO理論詳細解析和實戰案例

前言 Netty是什么&#xff1f; Netty 是一個基于 Java 的 ?高性能異步事件驅動網絡應用框架&#xff0c;主要用于快速開發可維護的協議服務器和客戶端。它簡化了網絡編程的復雜性&#xff0c;特別適合構建需要處理海量并發連接、低延遲和高吞吐量的分布式系統。 1)Netty 是…

TIM輸入捕獲知識部分

越往左&#xff0c;頻率越高&#xff1b;越往右&#xff0c;頻率越低。【越緊湊&#xff0c;相同時間&#xff0c;次數越多】 計算頻率的方法&#xff1a;測評法、測周法、中界頻率。 頻率的定義&#xff1a;1s內出現了多少個重復的周期 測評法就是從頻率的定義出發的&#…