Redis數據結構深度解析:從String到Stream的奇幻之旅(一)

Redis系列文章

《半小時掌握Redis核心操作:從零開始的實戰指南》-CSDN博客

Redis數據結構深度解析:從String到Stream的奇幻之旅(一)-CSDN博客

Redis數據結構深度解析:從String到Stream的奇幻之旅(二)-CSDN博客


提示:寫完文章后,目錄可以自動生成,如何生成可參考右邊的幫助文檔

Redis數據結構深度解析

Redis作為全球最受歡迎的內存數據庫,其核心競爭力不僅在于高性能的讀寫能力,更在于其靈活多變的數據結構體系。從最基礎的字符串(String)到革命性的消息流(Stream),Redis的數據結構如同瑞士軍刀般,為開發者提供了應對各類場景的“武器庫”。本文將帶您深入探索這些數據結構的底層原理、設計哲學以及實戰應用,揭開Redis的“奇幻之旅”。

一、String:輕量級存儲的SDS魔法

Redis的String類型看似簡單,實則一點也不容易。它基于SDS(Simple Dynamic String)結構實現,SDS不僅保存了實際字符串內容,還維護了字符串長度、已分配空間大小等元信息。完美解決了C語言字符串的天然缺陷,同時支持原子操作(如INCR/DECR),成為實現計數器、分布式鎖等場景的首選。

核心優勢

  1. O(1)獲取長度:通過len字段記錄字符串長度,避免遍歷計算。
  2. 二進制安全:無需依賴\0結尾,支持存儲任意二進制數據。
  3. 動態擴容與惰性釋放
    • 預分配:當字符串擴展時,按需分配額外空間(如擴展為原容量的1.5倍),減少頻繁分配開銷。
    • 惰性釋放:縮短字符串時保留未使用空間,供后續擴展復用。

內存優化編碼策略

編碼類型適用場景存儲方式優勢
int純整數字符串(如"10086")直接轉為long類型存儲,無需SDS結構內存節省80%,適合計數器場景
embstr字符串長度≤39字節(SDS+redisObject總長≤44B)將SDS結構與Redis對象頭合并為連續內存塊(避免內存碎片)高效小對象存儲(如Session ID)
raw大字符串(如文件緩存)獨立分配內存塊,SDS結構與數據分離支持超大容量,避免embstr限制

編碼自動切換示例

# 存儲小整數(觸發int編碼)  
SET counter 10086  # 內存占用約8字節(long類型)  # 存儲短字符串(觸發embstr編碼)  
SET user:1001 "Alice"  # SDS與對象頭合并為連續內存  # 存儲大文件(觸發raw編碼)  
SET image:avatar "<base64 encoded data>"  # 獨立分配大內存塊  

原子操作與分布式場景

Redis的原子性特性使其非常適合用于實現計數器和分布式鎖。通過INCR/DECR命令,可以輕松實現計數器功能。而SETNX結合EXPIRE則可以構建簡單的分布式鎖機制,確保在分布式環境下對資源的安全訪問。

  1. 計數器(原子增減)

    # 電商庫存管理(原子性保證)  
    INCR stock:product_1001  # 庫存+1  
    DECRBY stock:product_1001 5  # 批量減庫存(如秒殺場景)  
  2. 分布式鎖(SETNX+EXPIRE)

    # 實現30秒有效期的分布式鎖  
    SET lock:order_1234 "locked" NX EX 30  # NX=僅當鎖不存在時設置,EX=設置過期時間  
    # 釋放鎖(需確保線程安全)  
    DEL lock:order_1234  
  3. 值替換(GETSET)

    # 原子地獲取舊值并設置新值  
    GETSET cache:key "new_value"  # 返回舊值并更新為"new_value"  

實戰場景

  1. 緩存用戶Session

    # 存儲用戶Session(鍵值對形式)  
    SET session:1001 "{user_id:1001, username:'Alice', expire:1704537600}" EX 3600  
    # 獲取并刷新過期時間  
    GET session:1001  
    EXPIRE session:1001 3600  
  2. 小文件緩存(如配置文件)

    # 緩存Nginx配置文件內容  
    SET config:nginx "<文件內容>" EX 86400  
    # 高性能讀取避免磁盤IO  
    GET config:nginx  
  3. 分布式唯一ID生成

    # 使用INCR生成自增ID  
    INCR id:order  # 每次返回遞增的數值(如1001,1002,...)  
  4. 熱點數據緩存(如排行榜Top1)

    # 實時存儲當前最高分  
    SET highscore:game_1001 999999  

二、List:雙鏈表與壓縮包的智慧選擇

?List是鏈表結構,適用于實現隊列和棧。LPUSH/RPUSH分別用于在列表頭部或尾部添加元素,LPOP/RPOP用于移除并返回頭/尾元素,完美模擬了棧和隊列的操作。對于小型列表,Redis會自動使用ziplist以節省內存;而對于較大的列表,則轉換為quicklist7。

兩種底層實現

  1. ziplist(壓縮列表)
    將小數據量列表(默認≤8KB或≤512個元素)以緊湊的連續內存塊存儲。每個元素包含類型標記和長度字段,適合頻繁讀寫的小列表。
    優勢:內存效率高,適合會話隊列、排行榜等場景。

  2. linkedlist(雙向鏈表)
    當列表超過閾值時自動切換為雙向鏈表,支持無限長度,但內存占用更大。
    優勢LPOP/RPOP操作復雜度為O(1),常用于消息隊列(如訂單處理流水線)。

核心特性

  1. 有序性:元素按插入順序排列,支持通過索引訪問(LRANGE)。
  2. 高效雙端操作LPUSH/RPUSH(頭/尾添加)、LPOP/RPOP(頭/尾彈出)復雜度均為O(1)。
  3. 靈活編碼:根據數據量自動選擇ziplist或linkedlist,平衡內存與性能。
  4. 阻塞式彈出BLPOP/BRPOP支持阻塞等待消息,適用于消息隊列。

實戰案例(生產者-消費者模型)

利用Lists的阻塞式彈出操作(BRPOP/BLPOP),可以方便地實現生產者-消費者模式的消息隊列,確保消息不會丟失且能夠及時處理。

# 生產者:向訂單隊列發送消息  
RPUSH mq:orders "order_1001"  
RPUSH mq:orders "order_1002"  # 消費者:阻塞式消費消息(等待最多5秒)  
BRPOP mq:orders 5  # 若5秒內無消息返回空,否則返回消息  
> 1) "mq:orders"  
> 2) "order_1001"  # 處理消息后移入完成隊列  
RPUSH mq:finished_orders "order_1001"  
進階技巧
  1. 動態調整編碼閾值

    CONFIG SET list-max-ziplist-size 1024  # 擴大ziplist內存閾值  
    CONFIG SET list-max-ziplist-entries 1000  # 擴大ziplist元素數量閾值  

    適用場景:當業務需要存儲稍大規模的小元素列表時,可優化內存占用。

  2. 分頁遍歷列表

    LRANGE my_list 0 99     # 獲取前100條  
    LRANGE my_list 100 199  # 獲取下一頁  
  3. 原子性操作組合

    # 實現“如果列表長度≤1000,則添加新元素”  
    EVAL "if redis.call('LLEN', KEYS[1]) < 1000 then return redis.call('RPUSH', KEYS[1], ARGV[1]) else return 0 end" 1 my_list "new_item"  
  4. 阻塞彈出與超時控制

    BRPOP mq:orders 10  # 最多等待10秒,超時返回空  

三、Set:無序集合的高效管理

基本介紹

Redis的Set(集合類型) 是一種無序、不重復的字符串元素集合,支持快速的增刪查改和集合運算(交集、并集、差集)。其底層通過兩種編碼實現:

  1. intset:當所有元素為整數且數量≤512時,使用緊湊的整數數組存儲,元素按升序排列,內存效率高。
  2. hashtable:當元素類型混合(如字符串與整數共存)或數量超過閾值時,轉為哈希表存儲,鍵為元素值,值固定為NULL

核心特性

  • 自動去重:添加重復元素會被靜默忽略。
  • 高效集合運算:通過SINTER(交集)、SUNION(并集)、SDIFF(差集)等命令實現復雜邏輯。
  • 隨機訪問:通過SRANDMEMBER可隨機獲取元素,適合抽獎、隨機推薦場景。

內存優化策略
  1. intset編碼

    • 實現原理:元素按升序存儲在連續內存塊中,支持快速查找和遍歷。
    • 優勢
      • 查找、添加、刪除復雜度均為O(1)(基于二分查找)。
      • 內存占用極低,適合小規模整數集合。
    • 適用場景:存儲用戶ID、訂單編號等整數類型的唯一標識。
    SADD user_ids 1001 1002 1003  # 存儲用戶ID集合  
    SCARD user_ids                # 返回集合元素個數  
  2. hashtable編碼

    • 實現原理:基于哈希表實現,鍵為元素值,值固定為NULL
    • 優勢
      • 支持任意類型元素(字符串、浮點數等)。
      • 可擴展性高,適應大規模數據存儲。
    • 適用場景:混合類型元素或超大集合(如百萬級用戶標簽)。

實戰案例
# 用戶A關注列表  
SADD userA_followers 1001 1002 1003 1004  # 用戶B關注列表  
SADD userB_followers 1002 1003 1005 1006  # 計算共同好友(交集)  
SINTER userA_followers userB_followers  # 返回 [1002, 1003] 

性能與優化技巧
  1. 避免大集合全量拉取
    使用SSCAN命令迭代遍歷集合,避免因SMEMBERS返回大數據量導致內存溢出。

    SSCAN user_ids 0 COUNT 100 MATCH "user_*"  # 分批獲取匹配的元素  
  2. 集合運算性能優化

    • 交集/并集/差集操作的時間復雜度與集合大小相關,建議先對小集合執行運算。
    • 使用SORTED SET替代復雜多集合運算(如需按權重排序)。
  3. 編碼自動轉換
    Redis會根據數據變化自動切換編碼(如intset→hashtable),可通過OBJECT ENCODING命令查看當前編碼類型:

    OBJECT ENCODING user_ids  # 返回"intset"或"hashtable"  

四、Hash:鍵值對的極致壓縮

Hash類型是存儲對象的天然選擇,其內部實現了高效的空間利用率。每個字段都直接映射到哈希表中的一個位置,訪問速度極快。底層支持兩種編碼:

  1. ziplist編碼:字段名+值總大小≤64KB且字段數≤512時,以壓縮列表存儲,內存效率提升50%以上。
    示例HSET user:1001 name "Alice" age 25

  2. hashtable編碼:大對象自動切換為標準哈希表,支持快速增刪改查。

兩種編碼性能對比

場景ziplist(字段數≤512)hashtable
內存占用更低較高
單次操作速度略慢(需遍歷)更快
適用場景小對象緩存大對象存儲

用戶畫像存儲優化

# 傳統String存儲(需要序列化)
SET user:1001 "{'name':'Bob','age':28,'vip':true}"# Hash存儲(支持字段級操作)
HSET user:1001 name "Bob" age 28 vip true
HINCRBY user:1001 age 1  # 原子更新年齡

存儲效率對比

操作String方式Hash方式
讀取單個字段需反序列化整個對象HGET直接獲取
更新單個字段全量覆蓋局部更新
網絡傳輸量(1個字段)整個JSON字符串單個字段值

五、Sorted Set:跳躍表的優雅平衡

Redis的Sorted Set(有序集合) 是一種無重復成員按分數(score)排序的鍵值對集合。每個成員(member)關聯一個唯一分數(score),通過跳躍表(SkipList)哈希表(Hash Table) 的雙重結構實現高效操作。

  • 跳躍表:按分數(score)排序,支持O(logN)的范圍查詢(如ZRANGEBYSCORE)。
  • 哈希表:實現O(1)的成員存在性判斷(ZSCORE)。

核心特性

  1. 有序性:成員按分數從小到大自動排序,相同分數按插入順序排列。
  2. 唯一性:成員唯一,重復添加時會更新分數并保持順序。
  3. 高效范圍查詢:支持按分數范圍(ZRANGEBYSCORE)、排名范圍(ZRANGE)快速獲取數據。
  4. 原子操作ZADD/ZREM等命令保證操作原子性,適合高并發場景。
雙結構協作示例
ZADD leaderboard 95 "Alice" 88 "Bob"  # 插入成員時:  
# 1. 哈希表記錄"Alice"→指向跳躍表節點  
# 2. 跳躍表按score=95插入并維護有序性  

內存優化策略

Redis通過以下機制優化Sorted Set的內存使用:

  • 跳躍表壓縮
    • 跨度(span)字段:記錄相鄰節點間的距離,支持快速計算排名(如ZRANK
    • 層級自適應:根據數據量動態調整跳躍表層數,平衡查找效率與內存占用。
  • 哈希表與跳躍表的分離存儲
    • 成員與分數存儲在跳躍表節點中,哈希表僅存儲成員到節點的映射,避免重復存儲。
  • 小集合優化
    • 對于小規模Sorted Set,Redis可能使用ziplist編碼(連續內存塊)壓縮存儲,節省空間。
實戰案例

案例1:游戲排行榜(實時TOP100)

# 添加用戶得分  
ZADD game_leaderboard 95000 "PlayerA" 88000 "PlayerB"  # 獲取TOP10玩家及分數  
ZRANGE game_leaderboard 0 9 WITHSCORES  
> 1) "PlayerB" 2) "88000" 3) "PlayerA" 4) "95000"  # 動態更新得分(自動排序)  
ZADD game_leaderboard 99000 "PlayerB"  # PlayerB的分數更新為99000,排名上升  

案例2:新聞流按時間倒序展示

# 用時間戳作為分數,確保新消息在前  
ZADD news_feed 1704537600 "Article1" 1704537601 "Article2"  # 獲取最新10條新聞  
ZRANGEBYSCORE news_feed +inf -inf LIMIT 0 10 REV  

案例3:帶過期時間的實時排行榜

# 設置排行榜3600秒后過期  
EXPIRE game_leaderboard 3600  # 高頻更新時確保原子性  
ZADD game_leaderboard 99999 "PlayerC" INCR  # 分數遞增操作(類似計數器)  

案例4:去重消息隊列(結合Sorted Set與List)

# 新消息用時間戳作為分數,確保唯一  
ZADD message_queue 1704537600 "msg_1001"  # 定期清理過期消息  
ZREMRANGEBYSCORE message_queue -inf 1704537600-3600  # 將消息推入List消費  
BLPOP message_list 0  
性能與優化技巧
  1. 范圍查詢優化

    • 使用ZRANGEBYSCORE結合WITHSCORESLIMIT減少數據傳輸量。
    • 預先對分數排序,避免客戶端排序。
  2. 批量操作提升效率

    # 一次性添加多個成員  
    ZADD leaderboard 95 "Alice" 88 "Bob" 92 "Charlie"  
  3. 跳躍表層級監控

    OBJECT ENCODING leaderboard  # 返回"ziplist"或"skiplist"  
  4. 分布式場景擴展

    • 使用SORTED SET實現分布式計數器(如統計全球用戶活躍度)。
    • 結合Lua腳本保證跨鍵操作的原子性。

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

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

相關文章

【Java開發指南 | 第三十五篇】Maven + Tomcat Web應用程序搭建

讀者可訂閱專欄&#xff1a;Java開發指南 |【CSDN秋說】 文章目錄 前言Maven Tomcat Web應用程序搭建1、使用Maven構建新項目2、單擊項目&#xff0c;連續按兩次shift鍵&#xff0c;輸入"添加"&#xff0c;選擇"添加框架支持"3、選擇Java Web程序4、點擊&…

機器始終是一個機器:技術本質與哲學邊界

機器始終是一個機器&#xff1a;技術本質與哲學邊界 這句話揭示了人工智能發展中的核心矛盾——無論技術如何進步&#xff0c;機器的本質仍是基于規則與數據的計算系統。這種「機器性」既是其能力的源泉&#xff0c;也是其與生命體智能不可逾越的邊界的根源。以下從技術本質、…

JAVA編程【jvm垃圾回收的差異】

jvm垃圾回收的差異 JVM&#xff08;Java Virtual Machine&#xff09;的垃圾回收&#xff08;GC&#xff09;機制是自動管理內存的一種方式&#xff0c;能夠幫助開發者釋放不再使用的內存&#xff0c;避免內存泄漏和溢出等問題。不同的垃圾回收器&#xff08;GC&#xff09;有…

親測解決筆記本觸摸板使用不了Touchpad not working

這個問題可以通過FnFxx來解決&#xff0c;筆記本鍵盤上Fxx會有一個觸摸板圖標。如果不行應該玉藻設置中關了&#xff0c;打開即可。 解決辦法 在藍牙&#xff0c;觸摸板里打開即可。 Turn it on in settings。

RAG技術深度解析:從基礎Agent到復雜推理Deep Search的架構實踐

重磅推薦專欄: 《大模型AIGC》 《課程大綱》 《知識星球》 本專欄致力于探索和討論當今最前沿的技術趨勢和應用領域,包括但不限于ChatGPT和Stable Diffusion等。我們將深入研究大型模型的開發和應用,以及與之相關的人工智能生成內容(AIGC)技術。通過深入的技術解析和實踐經…

數據結構篇——串(String)

一、引入 在計算機中的處理的數據內容大致可分為以整形、浮點型等的數值處理和字符、字符串等的非數值處理。 今天我們主要學習的就是字符串數據。本章主要圍繞“串的定義、串的類型、串的結構及其運算”來進行串介紹與學習。 二、串的定義 2.1、串的基本定義 串&#xff08;s…

【智能體架構:Agent】LangChain智能體類型ReAct、Self-ASK的區別

1. 什么是智能體 將大語言模型作為一個推理引擎。給定一個任務&#xff0c; 智能體自動生成完成任務所需步驟&#xff0c; 執行相應動作&#xff08;例如選擇并調用工具&#xff09;&#xff0c; 直到任務完成。 2. 先定義工具&#xff1a;Tools 可以是一個函數或三方 API也…

OmniParser技術分析(一)

1.引言 通過上篇文章介紹 OmniParser:下一代純視覺UI自動化測試先驅相信大家已經對OmniParser有初步了解&#xff0c;接下來詳細介紹下OmniParser使用了哪些技術模型實現了對UI純視覺的檢測和理解。 2.整體方案 通過閱讀OmniParser提供的運行Demo代碼知道&#xff0c;其實整…

設計心得——繼承和實例

一、繼承的應用場景 在上篇文章分析了繼承的應用&#xff0c;本文反過來講繼承和實例。可以理解對上文的繼承進行一下基礎知識的鋪墊&#xff0c;繼承的應用場景非常多&#xff0c;典型的應用場景包括&#xff1a; 1、單純屬性的繼承 這種繼承非常常見&#xff0c;在前面也舉過…

從連接到交互:SDN 架構下 OpenFlow 協議的流程與報文剖析

在SDN架構中&#xff0c;交換機與控制器之間的通信基于 OpenFlow協議&#xff0c;其設計目的是實現控制平面與數據平面的解耦。以下是 交換機連接控制器 和 數據包進入交換機觸發交互 的詳細流程及協議報文分析&#xff1a; 一、交換機連接控制器的流程&#xff08;初始化階段&…

opentitan riscv

OpenTitan?是一個開源的硅根信任&#xff08;Root of Trust, RoT&#xff09;項目&#xff0c;旨在使硅RoT的設計和實現更加透明、可信和安全&#xff0c;適用于企業、平臺提供商和芯片制造商。該項目由lowRISC CIC管理&#xff0c;作為一個協作項目&#xff0c;旨在生產高質量…

R語言使用scitable包交互效應深度挖掘一個陌生數據庫

很多新手剛才是總是覺得自己沒什么可以寫的&#xff0c;自己不知道選什么題材進行分析&#xff0c;使用scitable包后這個完全不用擔心&#xff0c;選題多到你只會擔心你寫不完&#xff0c;寫得不夠快。 今天演示一下使用scitable包深度挖掘一個陌生數據庫 先導入R包和數據 li…

電腦內存智能監控清理,優化性能的實用軟件

軟件介紹 Memory cleaner是一款內存清理軟件。功能很強&#xff0c;效果很不錯。 Memory cleaner會在內存用量超出80%時&#xff0c;自動執行“裁剪進程工作集”“清理系統緩存”以及“用全部可能的方法清理內存”等操作&#xff0c;以此來優化電腦性能。 同時&#xff0c;我…

C#控制臺應用程序學習——3.8

一、語言概述 1、平臺相關性 C# 主要運行在.NET 平臺上。.NET 提供了一個龐大的類庫&#xff0c;C# 程序可以方便地調用這些類庫來實現各種功能&#xff0c;如文件操作、數據庫訪問、網絡通信等。 2、語法風格 C# 的語法與 C、C 和 Java 有一定的相似性。例如&#xff0c;它使用…

鴻蒙HarmonyOS-Navagation基本用法

Navagation基本用法 Navigation組件是路由導航的根視圖容器&#xff0c;一般作為Page頁面的根容器使用&#xff0c;其內部默認包含了標題欄&#xff0c;內容欄和公工具欄&#xff0c;其中內容區默認首頁顯示導航內容&#xff08;Navigation的子組件&#xff09;或非首頁顯示&am…

初階數據結構(C語言實現)——4.1棧

目錄 1.棧1.1棧的概念及結構1.2 棧的實現1.1.0 棧的初始化1.1.1 銷毀1.1.2 入棧1.1.3 出棧1.1.4 獲取棧中有效元素個數1.1.5 檢測棧是否為空&#xff0c;如果為空返回非零結果&#xff0c;如果不為空返回01.1.6 獲取棧頂元素1.1.7 驗證 附錄 棧的C語言實現源碼.h文件.c文件test…

計算光學成像與光學計算概論

計算光學成像所涉及研究的內容非常廣泛&#xff0c;雖然計算光學成像的研究內容是發散的&#xff0c;但目的都是一致的&#xff1a;如何讓相機記錄到客觀實物更豐富的信息&#xff0c;延伸并擴展人眼的視覺感知。總的來說&#xff0c;計算光學成像現階段已經取得了很多令人振奮…

什么樣的物聯網框架適合開展共享自助KTV唱歌項目?

現在物聯網的廣泛應用&#xff0c;也讓更多用戶們看到了它的實力&#xff0c;也使得共享經濟遍地開花。其中共享自助唱歌設備也備受歡迎&#xff0c;那么適合開展共享自助KTV唱歌項目的物聯網框架都應具備哪些特點呢&#xff1f; 智能化與自動化管理 物聯網技術在共享KTV中的應…

機器視覺選型中,不同焦距的鏡頭成像視野有什么不同?

不同焦距的鏡頭成像視野的差異主要體現在視角范圍和透視效果上。焦距越長&#xff0c;視角越窄&#xff0c;能捕捉的景物范圍越小&#xff1b;焦距越短&#xff0c;視角越廣&#xff0c;覆蓋的景物范圍越大。以下是具體分析&#xff1a; 焦距與視角的關系 焦距&#xff08;Foc…

Linux16-數據庫、HTML

數據庫&#xff1a; 數據存儲&#xff1a; 變量、數組、鏈表-------------》內存 &#xff1a;程序運行結束、掉電數據丟失 文件 &#xff1a; 外存&#xff1a;程序運行結束、掉電數據不丟失 數據庫&#xff1a; …