Redis對象編碼

前言

Redis中提供多種數據結構:string、list、map、set、zset等,關于上述多種數據類型的底層實現原理,Redis針對不同的數據類型對應的不同使用場景從時間和空間上進行平衡選擇不同的對象編碼方式。本文大致介紹一些Redis對象編碼方式以及在上述五種數據類型上的應用。

一、統一對象結構:redisObject

在Redis中所有的數據都通過redisObject結構體封裝,其核心字段定義如下:

typedef struct redisObject {unsigned type:4;            // 數據類型(string/list/hash/set/zset)unsigned encoding:4;        // 底層編碼(如int/ziplist/skiplist)unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or* LFU data (least significant 8 bits frequency* and most significant 16 bits access time). */// 內存淘汰信息int refcount;               // 引用計數void *ptr;                  // 指向實際數據的指針
} robj;

通過redisObject結構體中type字段和encoding字段分離,實現對不同的數據類型動態適配合適的編碼方式。用戶使用方式上,無需考慮底層編碼方式只需要按照需求選擇對應數據類型。

二、核心數據類型

1、簡單動態字符串(Simple Dynamic String - SDS)

Redis中描述字符串使用SDS,對比C語言傳統字符串,SDS在獲取字符串長度上有更快的速度優勢,同時保證二進制安全。C語言字符串以'\0'結尾,SDS通過len字段標識字符串長度,因此在存儲二進制數據時保證二進制安全。并且SDS兼容C語言字符串,在buf字段末尾保留'\0',可以兼容部分C語言字符串操作函數。具體實現如下:

struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
  • len :已使用字節長度(字符串實際長度,不包括'\0')

  • alloc:分配的總字節長度(不包括header)

  • flags:SDS類型標識

  • buf[]:柔性數組,存放具體的字符串 + '\0'

2、字典(Dict)

Redis中dict數據類型是使用拉鏈法處理hash沖突的hash表結構。其中關鍵數據結構為:dict、dictht、dictEntry。具體實現以及層級結構如下:

typedef struct dictEntry {void *key;                  //  key值union {void *val;uint64_t u64;int64_t s64;double d;} v;                        //  value值struct dictEntry *next;     //  指向下個元素,拉鏈法處理hash沖突
} dictEntry;                    //  元素
?
typedef struct dictType {uint64_t (*hashFunction)(const void *key);void *(*keyDup)(void *privdata, const void *key);void *(*valDup)(void *privdata, const void *obj);int (*keyCompare)(void *privdata, const void *key1, const void *key2);void (*keyDestructor)(void *privdata, void *key);void (*valDestructor)(void *privdata, void *obj);
} dictType;                     // dict操作函數集合
?
/* This is our hash table structure. Every dictionary has two of this as we* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {dictEntry **table;          //  元素數組unsigned long size;         //  hash表長度unsigned long sizemask;     //  用于映射位置的掩碼,值永遠等于 (size - 1),索引值=Hash值&掩碼值unsigned long used;         //  hash表當前包含的節點數量
} dictht;                       //  具體hash表結構
?
typedef struct dict {dictType *type;             //  該字典對應的特定操作函數 (hash, keyDup, keyCompare, ...)void *privdata;             //  上述類型函數對應的可選參數dictht ht[2];               //  兩張hash表,方便rehah操作long rehashidx; /* rehashing not in progress if rehashidx == -1 */      //  rehash標識unsigned long iterators; /* number of iterators currently running */    //  當前運行的迭代器數量
} dict;                         //  hash表

3、雙向鏈表(list)

實現結構同數據結構中雙向鏈表。具體實現如下:

typedef struct listNode {struct listNode *prev;struct listNode *next;void *value;
} listNode;
?
typedef struct list {listNode *head;listNode *tail;void *(*dup)(void *ptr);void (*free)(void *ptr);int (*match)(void *ptr, void *key);unsigned long len;
} list;

4、壓縮列表(ziplist)- Redis 7.0 起替換為 listpack

ziplist結構是一塊連續內存存儲一個字節數組,其中順序存儲多個元素,每個節點元素存儲一個字節數組或者一個整數。

  • zlbytes: 4 字節,列表總字節數(字節長度)。

  • zltail: 4 字節,列表尾節點偏移量。

  • zllen: 2 字節,節點數量 (超過 65535 時需遍歷)。

  • entry: 若干節點。每個節點包含:

    • prevlen: 前一個節點的長度 (1 或 5 字節)。

    • encoding: 內容編碼 (類型和長度,1/2/5 字節)。

    • content: 實際數據。

  • zlend: 1 字節,結束標志 0xFF(255)

對于節點的數據結構中prevlen記錄的是前一個節點的長度,此時如果調整當前節點,會影響后面一個節點的prevlen,此時如果是發生的擴展操作,那么可能會導致連續重新分配多個節點。此時該數據結構效率嚴重下降。因此這也是listpack替代它的主要原因。

5、緊湊列表(listpack) - Redis5.0引入,7.0開始列表/哈希/有序集合的ziplist實現替換為listpack

listpack是對ziplist的優化。其結構也是一塊連續內存。與ziplist的主要不同點為在節點元素中ziplist的節點長度信息保存的是前一個節點的長度,而ziplist保存的是自身的長度信息。

6、整數集合(intSet)

整數集合是一塊連續內存,其元素保存的是一個整數,其中通過encoding字段中標識了當前元素的整數類型,length保存了當前的元素數量,然后后續就挨個保存每個元素。

7、快速列表(quicklist)

快速列表是結合list與ziplist/listpack。list是一個雙向鏈表,ziplist/listpack是一塊連續的內存區域高效的保存數據。list每個節點的值保存一個ziplist/listpack結構。在具體操作時,插入/刪除操作多數集中在list節點內部,即ziplist/listpack中。當節點過大/過小時進行節點分裂/合并操作。

typedef struct quicklistNode {struct quicklistNode *prev;struct quicklistNode *next;unsigned char *zl;unsigned int sz; ? ? ? ? ? ? /* ziplist size in bytes */unsigned int count : 16; ? ? /* count of items in ziplist */unsigned int encoding : 2; ? /* RAW==1 or LZF==2 */unsigned int container : 2; ?/* NONE==1 or ZIPLIST==2 */unsigned int recompress : 1; /* was this node previous compressed? */unsigned int attempted_compress : 1; /* node can't compress; too small */unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
?
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count; ? ? ? ?/* total count of all entries in all ziplists */unsigned long len; ? ? ? ? ?/* number of quicklistNodes */int fill : QL_FILL_BITS; ? ? ? ? ? ? ?/* fill factor for individual nodes */unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];
} quicklist;

8、跳表(skiplist)

Redis中跳表主要用在zset的實現中。跳表數據結構是對時間和空間上的綜合。跳表是在鏈表基礎上提供更高效的查找效率(O(logN)),并且在實現上更簡單。

?更多資料:0voice · GitHub

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

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

相關文章

12-Django項目實戰-登錄短信驗證

1.路由配置 2.對接第三方短信接口 詳細內容請點擊 3.視圖函數 def sms_view(request):"""短信驗證視圖邏輯1.獲取請求體的數據[phone]2.調用封裝的短信發送接口,實現發送短信"""data json.loads(request.body)phone data.get(&q…

Java技術棧/面試題合集(11)-設計模式篇

場景 Java入門、進階、強化、擴展、知識體系完善等知識點學習、性能優化、源碼分析專欄分享: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/140870227 通過對面試題進行系統的復習可以對Java體系的知識點進行查漏補缺。 注: 博客: 霸道流氓氣質-CSDN博…

Linux系統:Ext系列文件系統(軟件篇)

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 文章目錄[TOC](文章目錄)一,ext2文件系統1-1 宏觀認識1-2 Block Group1-3 塊組內部構成1-3-1 超級塊(Super Block)1-3-2 塊組描述符表GDT(Group Descriptor Table…

14. isaacsim4.2教程-April Tags/給相機加噪聲

1. 前言April Tags 是一種視覺標簽(類似 QR 碼),用于通過相機進行定位和識別。它們通常用于計算機視覺任務中,幫助機器人識別和定位自己在物理空間中的位置,或者識別和追蹤特定對象。前提條件啟用 ROS 橋接&#xff1a…

Kafka + 時間輪 + 數據庫實現延遲隊列方案

Kafka 原生不支持延遲隊列功能。而RabbitMQ、RocketMQ及Redis等其他消息隊列原生支持延遲隊列。 RabbitMQ RocketMQ Redis 實現方式 通過插件實現,消息進入延遲隊列后根據配置時間過濾轉發。 原生支持,發送消息時設置延遲級別,定時任務處…

力扣 hot100 Day69

287. 尋找重復數 給定一個包含 n 1 個整數的數組 nums ,其數字都在 [1, n] 范圍內(包括 1 和 n),可知至少存在一個重復的整數。 假設 nums 只有 一個重復的整數 ,返回 這個重復的數 。 你設計的解決方案必須 不修改…

Android 的CameraX的使用(配置,預覽,拍照,圖像分析,錄視頻)

Android Studio 版本號:2024.1.2 CameraX是Jetpack系列中的一個庫,它基于Camera2 API構建,但提供了更高層次的抽象。 CameraX 三大核心用例: Preview預覽 ,ImageCapture拍照和 VideoCapture錄視頻 一、創建項目,進行環境配置 CameraX 需要一些屬于 Java 8 的方法,因此…

【機器學習深度學習】微調訓練數據質量

目錄 前言 一、為什么數據質量評估很重要 二、數據質量評估的核心維度 三、數據質量的可量化維度(必須要測的指標) 四、多答案、多類型數據的取舍與優化 場景 A:一個問題有多個相似回答 場景 B:多個類型數據,每…

從DeepSeek-V3到Kimi K2,大型語言模型架構對比

文章目錄 摘要 **稀疏化與專家系統** **注意力機制優化** **歸一化與穩定性設計** 模型架構對比詳析 DeepSeek-V3 vs Llama 4 Maverick Qwen3 vs SmolLM3 Kimi 2的突破 1 DeepSeek V3/R1 1.1 多頭潛在注意力(MLA) 1.2 混合專家系統(MoE) 1.3 DeepSeek 總結 2 OLMo 2 2.1 歸…

Unity筆記(二)——Time、Vector3、位置位移、角度、旋轉、縮放、看向

寫在前面寫本系列的目的(自用)是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解,方便自己以后快速復習,減少遺忘。這里只有部分語法知識。五、Time時間相關1、時間縮放比例概念:可以通過UnityEngine.Time類的timeScale屬性控制游戲時…

vue+vite項目中怎么定義一個環境變量可以在開發環境和生產環境使用不同的值,并且可以在vue頁面和index.html通用。

首先我們需要下載一個插件vite-plugin-html然后再項目最外層和index.html同級目錄下新建.env.development和.env.production兩個項目并且定義你想要的環境變量名:注意要以VITE_開頭VITE_APP_MAP_TOKEN1233444然后vite.config.js文件import { defineConfig,loadEnv } from vite…

Python-深度學習--2信息熵,條件熵(ID3決策樹),KL散度

一、信息熵(Entropy)的計算與應用信息熵用于衡量一個概率分布的不確定性,值越大表示分布越分散(不確定性越高)。1. 數學定義對于離散概率分布 P,信息熵公式為:(通常以 2 為底單位是比…

國產化Word處理控件Spire.Doc教程:Python提取Word文檔中的文本、圖片、表格等

在現代辦公場景中,Word文檔已成為信息存儲與交流的重要載體,承載著關鍵的業務數據、結構化表格、可視化圖表以及協作批注等重要內容。面對日益增長的文檔處理需求,傳統的人工操作方式已難以滿足效率與準確性的雙重標準。采用Python實現Word文…

Spring IOC 原理

Spring IoC(控制反轉)是Spring框架的核心機制,其原理是通過容器管理對象生命周期和依賴關系,實現解耦。 1. 控制反轉(IoC)核心思想 傳統模式:對象主動創建依賴(如new Service()&…

VSCode:基礎使用 / 使用積累

官網 Visual Studio Code - Code Editing. Redefined 記錄一、更新依賴 嘗試刪除yarn.lock文件 記錄二、“解決沖突”的方式變了 更新后,“解決沖突”的方式變了,有的時候能選中兩者,有的時候不能 現在又更新了,回復到了原來…

tcp 確認應答和超時時間

1. 確認應答之間的時間(RTT)這是指 從發送方發送數據到接收方返回確認(ACK)之間的時間。它反映的是數據傳輸的 往返延遲。例如,發送方發送一個數據包,接收方收到后,回傳一個確認包(A…

圖的應用-最短路徑

最短路徑的典型用途:交通網絡的問題——從甲地到乙地之間是否有公路連通?在有多條通路的情況下,哪一條路最短?交通網絡用有向網來表示:頂點——表示地點,弧——表示兩個地點有路連通,弧上的權值…

【qt5_study】1.Hello world

模板 作為初學者我們選擇第一個Application(Qt)和 Qt Widgets Application,所謂的模板就是 Qt為了方便開發程序,在新建工程時可以讓用戶基于一種模板來編寫程序,包括 cpp文件, ui文件都已經快速的創建,而不用用戶手動創建這些文件。 基類 這里默認選擇的基類為 QMainWin…

項目構想|文生圖小程序

Date: August 4, 2025項目介紹 👋,我們通過 Vibe Coding 做一個文字生成圖片的小程序。 我們會從需求分析、技術選型、UI設計、項目構筑到最后打包,一路嘗試 Vibe Coding 實現。 創建項目 創建文件夾:ai-pic-mini-app 采用 Git 進…

TiDB/MongoDB/Taosdb存儲引擎概覽

數據庫類型存儲引擎數據結構源碼位置tidbRockDBLSM樹https://github.com/facebook/rocksdbmongodbWiredTigerB 樹/LSM樹https://github.com/wiredtiger/wiredtigerTDengineTSDBBRINhttps://github.com/taosdata/TDengine 1、tidb存儲引擎概覽 LSM樹數據結構描述LSM樹(Log Str…