redis——數據結構(字典、鏈表、字符串)

1 字符串

redis并未使用傳統的c語言字符串表示,它自己構建了一種簡單的動態字符串抽象類型。

在redis里,c語言字符串只會作為字符串字面量出現,用在無需修改的地方。

當需要一個可以被修改的字符串時,redis就會使用自己實現的SDS(simple dynamic string)。比如在redis數據庫里,包含字符串的鍵值對底層都是SDS實現的,不止如此,SDS還被用作緩沖區(buffer):比如AOF模塊中的AOF緩沖區以及客戶端狀態中的輸入緩沖區。

下面來具體看一下sds的實現:

struct sdshdr
{int len;//buf已使用字節數量(保存的字符串長度)int free;//未使用的字節數量char buf[];//用來保存字符串的字節數組
};

sds遵循c中字符串以'\0'結尾的慣例,這一字節的空間不算在len之內。

這樣的好處是,我們可以直接重用c中的一部分函數。比如printf;

? ? sds相對c的改進

? ? 獲取長度:c字符串并不記錄自身長度,所以獲取長度只能遍歷一遍字符串,redis直接讀取len即可。

? ? 緩沖區安全:c字符串容易造成緩沖區溢出,比如:程序員沒有分配足夠的空間就執行拼接操作。而redis會先檢查sds的空間是否滿足所需要求,如果不滿足會自動擴充。

? ? 內存分配:由于c不記錄字符串長度,對于包含了n個字符的字符串,底層總是一個長度n+1的數組,每一次長度變化,總是要對這個數組進行一次內存重新分配的操作。因為內存分配涉及復雜算法并且可能需要執行系統調用,所以它通常是比較耗時的操作。? ?

? ? redis內存分配:

1、空間預分配:如果修改后大小小于1MB,程序分配和len大小一樣的未使用空間,如果修改后大于1MB,程序分配? 1MB的未使用空間。修改長度時檢查,夠的話就直接使用未使用空間,不用再分配。?

2、惰性空間釋放:字符串縮短時不需要釋放空間,用free記錄即可,留作以后使用。

? ? 二進制安全

c字符串除了末尾外,不能包含空字符,否則程序讀到空字符會誤以為是結尾,這就限制了c字符串只能保存文本,二進制文件就不能保存了。

而redis字符串都是二進制安全的,因為有len來記錄長度。

2 鏈表

作為一種常用數據結構,鏈表內置在很多高級語言中,因為c并沒有,所以redis實現了自己的鏈表。

鏈表在redis也有一定的應用,比如列表鍵的底層實現之一就是鏈表。(當列表鍵包含大量元素或者元素都是很長的字符串時)

發布與訂閱、慢查詢、監視器等功能也用到了鏈表。

具體實現:

//redis的節點使用了雙向鏈表結構
typedef struct listNode {// 前置節點struct listNode *prev;// 后置節點struct listNode *next;// 節點的值void *value;
} listNode;
//其實學過數據結構的應該都實現過
typedef struct list {// 表頭節點listNode *head;// 表尾節點listNode *tail;// 鏈表所包含的節點數量unsigned long len;// 節點值復制函數void *(*dup)(void *ptr);// 節點值釋放函數void (*free)(void *ptr);// 節點值對比函數int (*match)(void *ptr, void *key);
} list;

總結一下redis鏈表特性:

雙端、無環、帶長度記錄、

多態:使用?void*?指針來保存節點值, 可以通過?dup?、?free?、?match?為節點值設置類型特定函數, 可以保存不同類型的值。

3、字典

其實字典這種數據結構也內置在很多高級語言中,但是c語言沒有,所以redis自己實現了。

應用也比較廣泛,比如redis的數據庫就是字典實現的。不僅如此,當一個哈希鍵包含的鍵值對比較多,或者都是很長的字符串,redis就會用字典作為哈希鍵的底層實現。

來看看具體是實現:

//redis的字典使用哈希表作為底層實現
typedef struct dictht {// 哈希表數組dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩碼,用于計算索引值// 總是等于 size - 1unsigned long sizemask;// 該哈希表已有節點的數量unsigned long used;} dictht;

table?是一個數組, 數組中的每個元素都是一個指向dictEntry?結構的指針, 每個?dictEntry?結構保存著一個鍵值對

圖為一個大小為4的空哈希表。

我們接著就來看dictEntry的實現:

typedef struct dictEntry {// 鍵void *key;// 值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下個哈希表節點,形成鏈表struct dictEntry *next;
} dictEntry;

(v可以是一個指針, 或者是一個?uint64_t?整數, 又或者是一個?int64_t?整數。)

next就是解決鍵沖突問題的,沖突了就掛后面,這個學過數據結構的應該都知道吧,不說了。

?

下面我們來說字典是怎么實現的了。

typedef struct dict {// 類型特定函數dictType *type;// 私有數據void *privdata;// 哈希表dictht ht[2];// rehash 索引int rehashidx; //* rehashing not in progress if rehashidx == -1 
} dict;

type?和?privdata?是對不同類型的鍵值對, 為創建多態字典而設置的:

type?指向?dictType?, 每個?dictType?保存了用于操作特定類型鍵值對的函數, 可以為用途不同的字典設置不同的類型特定函數。

而?privdata?屬性則保存了需要傳給那些類型特定函數的可選參數。

而dictType就暫時不展示了,不重要而且字有點多。。。還是講有意思的東西吧

? ? rehash(重新散列)

隨著我們不斷的操作,哈希表保存的鍵值可能會增多或者減少,為了讓哈希表的負載因子維持在合理的范圍內,有時需要對哈希表進行合理的擴展或者收縮。?一般情況下, 字典只使用?ht[0]?哈希表,?ht[1]?哈希表只會在對?ht[0]?哈希表進行 rehash 時使用。

redis字典哈希rehash的步驟如下:

1)為ht[1]分配合理空間:如果是擴展操作,大小為第一個大于等于ht[0]*used*2的,2的n次冪。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果是收縮操作,大小為第一個大于等于ht[0]*used的,2的n次冪。

2)將ht[0]中的數據rehash到ht[1]上。

3)釋放ht[0],將ht[1]設置為ht[0],ht[1]創建空表,為下次做準備。

? ? 漸進rehash

數據量特別大時,rehash可能對服務器造成影響。為了避免,服務器不是一次性rehash的,而是分多次。

我們維持一個變量rehashidx,設置為0,代表rehash開始,然后開始rehash,在這期間,每個對字典的操作,程序都會把索引rehashidx上的數據移動到ht[1]。

隨著操作不斷執行,最終我們會完成rehash,設置rehashidx為-1.

需要注意:rehash過程中,每一次增刪改查也是在兩個表進行的。

?

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

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

相關文章

Hotspot虛擬機的對象

創建 Step1:類加載檢查 虛擬機遇到一條 new 指令時,首先將去檢查這個指令的參數是否能在常量池中定位到這個類的符號引用,并且檢查這個符號引用代表的類是否已被加載過、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。 Step2:分…

劍指offer(刷題1-10)--c++,Python版本

文章目錄目錄第一題:解題思路:代碼實現:c順序查找二分查找Python第二題:解題思路:代碼實現:cpython第三題:解題思路:代碼實現:c使用棧輔助反轉鏈表python第四題&#xff…

redis——數據結構(整數集合,壓縮列表)

4、整數集合 整數集合(intset)是 Redis 用于保存整數值的集合抽象數據結構, 可以保存 int16_t 、 int32_t 、 int64_t 的整數值, 并且保證集合中不會出現重復元素。 實現較為簡單: typedef struct intset {// 編碼方…

原 劍指offer(刷題11-20)--c++,Python版本

文章目錄目錄第11題:解題思路:代碼實現:cpython第12題:解題思路:代碼實現:cpython第13 題:解題思路:代碼實現:cpython第 14題:解題思路:代碼實現&…

LRU介紹和實現

LRU全稱是Least Recently Used,即最近最久未使用的意思。 LRU算法的設計原則是:如果一個數據在最近一段時間沒有被訪問到,那么在將來它被訪問的可能性也很小。也就是說,當限定的空間已存滿數據時,應當把最久沒有被訪問…

機器學習知識總結系列- 知識圖譜(0-0)

文章目錄目錄機器學習知識圖譜目錄 本系列的文章只是根據個人的習慣進行總結,可能結構與一些書籍上不太一樣,開始的內容比較簡單,會隨著后續的深入,不斷豐富和更新圖譜,同時也期待有相同興趣的朋友一起給我留言一起豐富…

跳表介紹和實現

想慢慢的給大家自然的引入跳表。 想想,我們 1)在有序數列里搜索一個數 2)或者把一個數插入到正確的位置 都怎么做? 很簡單吧 對于第一個操作,我們可以一個一個比較,在數組中我們可以二分,這…

機器學習知識總結系列- 基本概念(1-0)

文章目錄目錄1. 機器學習的定義2. 機器學習的分類2.1根據是否在人類監督下進行訓練監督學習非監督學習半監督學習強化學習2.2根據是否可以動態漸進的學習在線學習批量學習2.3根據是否在訓練數據過程中進行模式識別實例學習基于模型的學習3. 機器學習中的一些常見名詞4. 機器學習…

劍指offer(刷題21-30)--c++,Python版本

文章目錄目錄第 21題:解題思路:代碼實現:cpython第22 題:解題思路:代碼實現:cpython第23 題:解題思路:代碼實現:cpython第24 題:解題思路:代碼實現…

redis——對象

剛寫了redis主要的數據結構: 動態字符串、雙端鏈表、字典、壓縮列表、整數集合、跳表等 redis肯定不能直接使用這些數據結構來實現數據庫,它用這些數據庫建立了一個對象系統,包含: 字符串對象、列表對象、哈希對象、集合對象、…

劍指offer(刷題31-40)--c++,Python版本

文章目錄目錄第31 題:解題思路:代碼實現:cpython第32題:解題思路:代碼實現:cpython第33題:解題思路:代碼實現:cpython第34題:解題思路:代碼實現&a…

redis——數據庫

redis服務器將所有數據庫都保存在redis/redisServer中,數組db存放所有數據庫,每一項是一個redisdb結構。dbnum代表數據庫數量。 客戶端有一個指針指向當前數據庫,可以切換,也就是移動指針。 鍵空間 現在稍微介紹一下redisdb結構…

劍指offer(刷題41-50)--c++,Python版本

文章目錄目錄第41題:解題思路:代碼實現:cpython第42題:解題思路:代碼實現:cpython第43題:解題思路:代碼實現:cpython第44題:解題思路:代碼實現&am…

redis——持久化

因為redis是內存數據庫,他把數據都存在內存里,所以要想辦法實現持久化功能。 RDB RDB持久化可以手動執行,也可以配置定期執行,可以把某個時間的數據狀態保存到RDB文件中,反之,我們可以用RDB文件還原數據庫…

redis原理總結

數據結構(字典、鏈表、字符串) 數據結構(整數集合,壓縮列表) 數據結構(跳表介紹和手撕) LRU介紹和實現 對象(字符串對象、列表對象、哈希對象、集合對象、有序集合總結&#xff…

劍指offer(刷題51-60)--c++,Python版本

文章目錄目錄第51題:解題思路:代碼實現:cpython第52題:解題思路:代碼實現:cpython第53題:解題思路:代碼實現:cpython第54題:解題思路:代碼實現&am…

2017第一屆河北省大學生程序設計競賽題解

超級密碼 小明今年9歲了,最近迷上了設計密碼!今天,他又設計了一套他認為很復雜的密碼,并且稱之為“超級密碼”. 說實話,這套所謂的“超級密碼”其實并不難:對于一個給定的字符串,你只要提取其中…

劍指offer(刷題61-65)--c++,Python版本

文章目錄目錄第61題:解題思路:代碼實現:cpython第62題:解題思路:代碼實現:cpython第63題:解題思路:代碼實現:cpython第64題:解題思路:代碼實現&am…

2018第二屆河北省大學生程序設計競賽題解

icebound的賬單 題目描述 icebound從小就有記賬的習慣。又到了月末icebound統計資金狀況的時候。icebound每個月除了不停的揮霍以外,有時他會良心發現,勤工儉學,因此會有一些微薄的收入。然而icebound數學不好,需要你來幫助他統計…

大數的四則運算(加法、減法、乘法、除法)

大數的四則運算(加法、減法、乘法、除法) 前言: 在計算機中數字表示的范圍是有限制的,比如我們熟知的 int、float、double 等數據類型所能表示的范圍都是有限的,如果我們要對位數達到幾十位、幾百位、上千位的大整數進…