Redis 基本型別及功能

2021-06-23 01:33:30 字數 3373 閱讀 4101

一、鍊錶(adlist.h/adlist.c)

實質是乙個雙端列表

listiter是訪問鍊錶的迭代器,指標(next)指向鍊錶的某個結點,direction標示迭代訪問的方向(巨集al_start_head表示向前,al_start_tail表示向後)。

typedef struct listiter listiter;

二、字串(sds.h/sds.c)

typedef char *sds;

struct sdshdr ;

在sdsnewlen(const void *init, size_t initlen)中,會分配sizeof(struct sdshdr)+initlen+1的空間,並將*init指向的字串拷貝到buf中,在buf末尾補上』\0』。但是sdsnewlen()中返回給外部的,只有sdshdr->buf。

size_t sdslen(const sds s)

此處引數s實際是sdshdr中的buf,通過(s-(sizeof(struct sdshdr))即可得到sdshdr的位址。

三、雜湊表(dict.h/dict.c)

redis的雜湊表最大的特色就是自動擴容。當它的雜湊表容量不夠時,可以0/1切換,然後自動擴容。下面具體分析雜湊表的實現。

整個雜湊系統由結構體dict定義,其中type包含一系列雜湊表需要用的函式,dictht型別的陣列ht[2]表示兩個雜湊表例項,由rehashidx指明下乙個需要擴容的雜湊例項的編號,iterators記錄外部使用雜湊表的迭代器的數目。

typedef struct dict dict;

dictht為雜湊表具體實現的結構體,table指向雜湊中的記錄,用陣列+開鏈的形式儲存記錄;size表示雜湊表桶的大小,為2的指數;sizemark=size-1,方便雜湊值根據size取模;used記錄了雜湊表中的記錄數目。

typedef struct dictht dictht;

雜湊表使用開鏈的方式處理衝突,每條記錄都是鍊錶中的乙個結點。dicttype在雜湊系統中包含了一系列可由應用程式定義的函式指標,包括hash函式、key複製、value複製、key比較、key析構、value析構,以增加雜湊系統的靈活性。 其中系統定義了三種預設的type,表示最常用的三種雜湊表。

typedef struct dicttype dicttype;

extern dicttype dicttypeheapstringcopykey;

extern dicttype dicttypeheapstrings;

extern dicttype dicttypeheapstringcopykeyvalue;

redis定義了一系列的巨集用於操作雜湊表,例如設定記錄的value。對外提供的api,除了常規的建立雜湊表,增、刪、改記錄之外,有兩類是比較特別的:自動擴容和迭代器。

自動擴容

redis用變數dict_can_resize記錄雜湊是否可以自動擴容,由兩個方法dictenableresize()和dictdisableresize()設定該變數。應用程式可以使用dictresize()擴容,它首先判斷是否允許擴容,及是否正在擴容。若可以擴容,則呼叫dictexpand()擴容。然後應用程式需要呼叫dictrehashmilliseconds()啟動擴容過程,並指定擴容過程中記錄拷貝速度。除了應用程式指定的擴容外,在呼叫dictadd()往雜湊中新增記錄時,系統也會通過呼叫_dictexpandifneeded()判斷是否需要擴容。_dictexpandifneeded()中,如果正在擴容,則不會重複進行擴容;如果雜湊表size=0,即桶數目為0,則擴容到初始大小;否則如果used>=size,並且can_resize==1或used/size超過閥值(預設為5)時,以max(used, size)的兩倍為基數,呼叫dictexpand()擴容。

dictexpand()進行擴容時,會先選擇乙個滿足size需求的2的指數,然後分配記憶體空間,建立新的雜湊表。如果此時ht[0]為空,則直接將雜湊表賦值給ht[0];否則,賦值給ht[1],並啟動拷貝過程,將ht[0]的記錄逐個桶地拷貝到ht[1]中。置rehashidx=0,表明正在擴容,且待拷貝的桶為ht[0]->table[rehashidx]。

dictisrehashing()通過rehashidx來判斷是否正在擴容。這個方法在多處被呼叫,當dictadd()往雜湊表中新增記錄時,也會通過該方法判斷是否正在擴容。若正在擴容,則呼叫_dictrehashstep(),該函式判斷,若此時iterators==0,即沒有迭代器時,就從ht[0]中拷貝一部分記錄到ht[1]。拷貝過程在dictrehash()中完成,該函式返回0時,表示擴容結束,ht[0]中所有記錄都已拷貝到ht[1],且rehashidx被置為-1;否則返回1,表示擴容未結束。拷貝過程中,將ht[0]->table[rehashidx]拷貝到ht[1]後,rehashidx++,直到used==0,即所有記錄拷貝完成。拷貝乙個桶時,需要對桶中所有元素重新求雜湊值,然後乙個個放入ht[1]中。dictrehash()通過引數,控制每次拷貝的桶的數目。

迭代器迭代器提供了遍歷雜湊表中所有元素的方法,通過dictgetiterator()獲得迭代器後,使用dictnext(dictiterator *)方法獲得下乙個元素。當外部持有的迭代器數目不為0時,雜湊表會暫停擴容操作。迭代器遍歷的過程,從ht[0]開始,依次從第乙個桶table[0]開始遍歷桶中的元素,然後時table[1], table[2], ..., table[size],若正在擴容,則會繼續遍歷ht[1]中的桶。遍歷桶中元素時,依次訪問鍊錶中的每個元素。

四、記憶體(zmalloc.h/zmalloc.h)

先回憶各個系統中常見的記憶體分配函式:malloc()分配一塊指定大小的記憶體區域,並返回指向區域開頭的指標,若分配失敗,則返回null。

calloc()與malloc()一樣,分配一塊指定大小的記憶體區域,成功時返回區域頭指標,失敗返回null。區別在於,calloc()的輸入引數為count和size,即分配的項的數目,及每一項的大小。calloc()在成功分配記憶體空間後,會將空間內所有值置0。realloc()修改已分配的記憶體塊的大小。若已分配的記憶體塊後沒有足夠的空間用於擴充套件記憶體塊,則重新申請一塊滿足需要的記憶體塊,並將舊的資料拷貝到新位置,釋放舊的記憶體塊,返回指向新的記憶體塊的指標;否則直接擴充套件原有的記憶體塊。若分配失敗,返回null。free()釋放已分配的記憶體塊。

redis在申請記憶體時,除了申請需要的size外,還會多申請一塊定長(prefix_size)的區域用於記錄所申請的記憶體塊的長度。如果申請成功,redis會使用巨集函式(redis中為效能考慮,大量使用巨集函式)update_zmalloc_stat_alloc(size+prefix_size, size)記錄申請的記憶體塊的相關資訊,以便監控記憶體使用狀況;當記憶體塊被zfree()釋放時,根據頭部的資訊可以快速地獲知被釋放的記憶體區域的長度,然後通過巨集函式update_zmalloc_stat_free()標記釋放。在本身支援malloc_size()的系統中,prefix_size等於0.

資料基本型別及對應基本型別類

基本資料型別 char 16位 byte 8位 short 16位 int 32位 long,float,double,boolean 基本資料型別對應類 character,byte,short,integer,long,float,double,boolean,string 幾乎所有型別類都有相...

Redis 基本型別之 Set 型別

基本指令 sadd 127.0.0.1 6379 sadd myset hello 在集合中新增元素 integer 1 127.0.0.1 6379 sadd myset iron integer 1 127.0.0.1 6379 sadd myset egg integer 1 smembers...

Redis 基本型別與理解

一 字串 set key val k v mset k v k v k v type string 二 雜湊資料 hset key n val key n v hmset key n v n v n v type hash 三 列表 lpush key val k v 追加形式,可新增重複的元素 t...