日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

redis内部数据结构深入浅出

發(fā)布時(shí)間:2024/9/5 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis内部数据结构深入浅出 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最大感受,無論從設(shè)計(jì)還是源碼,Redis都盡量做到簡(jiǎn)單,其中運(yùn)用到的原理也通俗易懂。特別是源碼,簡(jiǎn)潔易讀,真正做到clean and clear,?這篇文章以u(píng)nstable分支的源碼為基準(zhǔn),先從大體上整理Redis的對(duì)象類型以及底層編碼。?當(dāng)我們?cè)诒疚闹刑岬絉edis的“數(shù)據(jù)結(jié)構(gòu)”,可能是在兩個(gè)不同的層面來討論它。

  • 第一個(gè)層面,是從使用者的角度,string,list,hash,set,sorted set
  • 第二個(gè)層面,是從內(nèi)部實(shí)現(xiàn)的角度,屬于更底層的實(shí)現(xiàn),? ?ht(dict),raw,embstr,intset,sds,ziplist,quicklist,skiplist

在討論任何一個(gè)系統(tǒng)的內(nèi)部實(shí)現(xiàn)的時(shí)候,我們都要先明確它的設(shè)計(jì)原則,這樣我們才能更深刻地理解它為什么會(huì)進(jìn)行如此設(shè)計(jì)的真正意圖。

  • 存儲(chǔ)效率(memory efficiency)。Redis是專用于存儲(chǔ)數(shù)據(jù)的,它對(duì)于計(jì)算機(jī)資源的主要消耗就在于內(nèi)存,因此節(jié)省內(nèi)存是它非常非常重要的一個(gè)方面。這意味著Redis一定是非常精細(xì)地考慮了壓縮數(shù)據(jù)、減少內(nèi)存碎片等問題。

  • 快速響應(yīng)時(shí)間(fast response time)。與快速響應(yīng)時(shí)間相對(duì)的,是高吞吐量(high throughput)。Redis是用于提供在線訪問的,對(duì)于單個(gè)請(qǐng)求的響應(yīng)時(shí)間要求很高,因此,快速響應(yīng)時(shí)間是比高吞吐量更重要的目標(biāo)。有時(shí)候,這兩個(gè)目標(biāo)是矛盾的。

  • 單線程(single-threaded)。Redis的性能瓶頸不在于CPU資源,而在于內(nèi)存訪問和網(wǎng)絡(luò)IO。而采用單線程的設(shè)計(jì)帶來的好處是,極大簡(jiǎn)化了數(shù)據(jù)結(jié)構(gòu)和算法的實(shí)現(xiàn)。相反,Redis通過異步IO和pipelining等機(jī)制來實(shí)現(xiàn)高速的并發(fā)訪問。顯然,單線程的設(shè)計(jì),對(duì)于單個(gè)請(qǐng)求的快速響應(yīng)時(shí)間也提出了更高的要求。

比如:Redis一個(gè)重要的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu):dict。

  • dict是一個(gè)用于維護(hù)key和value映射關(guān)系的數(shù)據(jù)結(jié)構(gòu),與很多語言中的Map或dictionary類似。Redis的一個(gè)database中所有key到value的映射,就是使用一個(gè)dict來維護(hù)的。不過,這只是它在Redis中的一個(gè)用途而已,它在Redis中被使用的地方還有很多。比如,一個(gè)Redis hash結(jié)構(gòu),當(dāng)它的field較多時(shí),便會(huì)采用dict來存儲(chǔ)。再比如,Redis配合使用dict和skiplist來共同維護(hù)一個(gè)sorted set

  • dict本質(zhì)上是為了解決算法中的查找問題(Searching),一般查找問題的解法分為兩個(gè)大類:一個(gè)是基于各種平衡樹,一個(gè)是基于哈希表。我們平常使用的各種Map或dictionary,大都是基于哈希表實(shí)現(xiàn)的。在不要求數(shù)據(jù)有序存儲(chǔ),且能保持較低的哈希值沖突概率的前提下,基于哈希表的查找性能能做到非常高效,接近O(1),而且實(shí)現(xiàn)簡(jiǎn)單。

  • dict也是一個(gè)基于哈希表的算法。和傳統(tǒng)的哈希算法類似,它采用某個(gè)哈希函數(shù)從key計(jì)算得到在哈希表中的位置,采用拉鏈法解決沖突,并在裝載因子(load factor)超過預(yù)定值時(shí)自動(dòng)擴(kuò)展內(nèi)存,引發(fā)重哈希(rehashing)。Redis的dict實(shí)現(xiàn)最顯著的一個(gè)特點(diǎn),就在于它的重哈希。它采用了一種稱為增量式重哈希(incremental rehashing)的方法,在需要擴(kuò)展內(nèi)存時(shí)避免一次性對(duì)所有key進(jìn)行重哈希,而是將重哈希操作分散到對(duì)于dict的各個(gè)增刪改查的操作中去。這種方法能做到每次只對(duì)一小部分key進(jìn)行重哈希,而每次重哈希之間不影響dict的操作。dict之所以這樣設(shè)計(jì),是為了避免重哈希期間單個(gè)請(qǐng)求的響應(yīng)時(shí)間劇烈增加,這與前面提到的“快速響應(yīng)時(shí)間”的設(shè)計(jì)原則是相符的。

一、對(duì)象類型

redis 是 key-value 存儲(chǔ)系統(tǒng),其中 key 類型一般為字符串,而 value 類型則為 redis 對(duì)象(redis object),可以綁定各種類型的數(shù)據(jù),譬如 string、list 和set,redis.h 中定義了 struct redisObject,它是一個(gè)簡(jiǎn)單優(yōu)秀的數(shù)據(jù)結(jié)構(gòu)

#define LRU_BITS 24 #define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */ #define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */typedef struct redisObject {//對(duì)象的數(shù)據(jù)類型,占4bits,共5種類型unsigned type:4; //對(duì)象的編碼類型,占4bits,共10種類型unsigned encoding:4;//least recently used//實(shí)用LRU算法計(jì)算相對(duì)server.lruclock的LRU時(shí)間unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) *///引用計(jì)數(shù)int refcount;//指向底層數(shù)據(jù)實(shí)現(xiàn)的指針void *ptr; } robj;//type的占5種類型: /* Object types */ #define OBJ_STRING 0 //字符串對(duì)象 #define OBJ_LIST 1 //列表對(duì)象 #define OBJ_SET 2 //集合對(duì)象 #define OBJ_ZSET 3 //有序集合對(duì)象 #define OBJ_HASH 4 //哈希對(duì)象/* Objects encoding. Some kind of objects like Strings and Hashes can be* internally represented in multiple ways. The 'encoding' field of the object* is set to one of this fields for this object. */ // encoding 的10種類型 #define OBJ_ENCODING_RAW 0 /* Raw representation */ //原始表示方式,字符串對(duì)象是簡(jiǎn)單動(dòng)態(tài)字符串 #define OBJ_ENCODING_INT 1 /* Encoded as integer */ //long類型的整數(shù) #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ //字典 #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ //不在使用 #define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ //雙端鏈表,不在使用 #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ //壓縮列表 #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */ //整數(shù)集合 #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ //跳躍表和字典 #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */ //embstr編碼的簡(jiǎn)單動(dòng)態(tài)字符串 #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */ //由壓縮列表組成的雙向列表-->快速列表

其中,void *ptr 已經(jīng)給了我們無限的遐想空間了(把最后一個(gè)指針留給了真正的數(shù)據(jù))

每種類型的對(duì)象至少都有兩種或以上的encoding方式,不同編碼可以在不同的使用場(chǎng)景上優(yōu)化對(duì)象的使用場(chǎng)景,用TYPE命令可查看某個(gè)鍵值對(duì)的類型

二、對(duì)象編碼

?不同類型和編碼的對(duì)象

REDIS_STRING REDIS_ENCODING_INT 使用整數(shù)值實(shí)現(xiàn)的字符串對(duì)象。 REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 編碼的簡(jiǎn)單動(dòng)態(tài)字符串實(shí)現(xiàn)的字符串對(duì)象。 REDIS_STRING REDIS_ENCODING_RAW 使用簡(jiǎn)單動(dòng)態(tài)字符串實(shí)現(xiàn)的字符串對(duì)象。 REDIS_LIST REDIS_ENCODING_ZIPLIST 使用壓縮列表實(shí)現(xiàn)的列表對(duì)象。 REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用雙端鏈表實(shí)現(xiàn)的列表對(duì)象。 REDIS_HASH REDIS_ENCODING_ZIPLIST 使用壓縮列表實(shí)現(xiàn)的哈希對(duì)象。 REDIS_HASH REDIS_ENCODING_HT 使用字典實(shí)現(xiàn)的哈希對(duì)象。 REDIS_SET REDIS_ENCODING_INTSET 使用整數(shù)集合實(shí)現(xiàn)的集合對(duì)象。 REDIS_SET REDIS_ENCODING_HT 使用字典實(shí)現(xiàn)的集合對(duì)象。 REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用壓縮列表實(shí)現(xiàn)的有序集合對(duì)象。 REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳躍表和字典實(shí)現(xiàn)的有序集合對(duì)象。

OBJECT ENCODING?對(duì)不同編碼的輸出

整數(shù) REDIS_ENCODING_INT "int" embstr 編碼的簡(jiǎn)單動(dòng)態(tài)字符串(SDS) REDIS_ENCODING_EMBSTR "embstr" 簡(jiǎn)單動(dòng)態(tài)字符串 REDIS_ENCODING_RAW "raw" 字典 REDIS_ENCODING_HT "hashtable" 雙端鏈表 REDIS_ENCODING_LINKEDLIST "linkedlist" 壓縮列表 REDIS_ENCODING_ZIPLIST "ziplist" 整數(shù)集合 REDIS_ENCODING_INTSET "intset" 跳躍表和字典 REDIS_ENCODING_SKIPLIST "skiplist" 

本質(zhì)上,Redis就是基于這些數(shù)據(jù)結(jié)構(gòu)而構(gòu)造出一個(gè)對(duì)象存儲(chǔ)系統(tǒng)。

關(guān)于redisObject?

  • ptr指針,指向?qū)ο蟮牡讓訉?shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)

  • encoding屬性記錄對(duì)象所使用的編碼

  • 淘汰時(shí)鐘,Redis 對(duì)數(shù)據(jù)集占用內(nèi)存的大小有「實(shí)時(shí)」的計(jì)算,當(dāng)超出限額時(shí),會(huì)淘汰超時(shí)的數(shù)據(jù)

  • 引用計(jì)數(shù),一個(gè) Redis 對(duì)象可能被多個(gè)指針引用。當(dāng)需要增加或者減少引用的時(shí)候,必須調(diào)用相應(yīng)的函數(shù),程序員必須遵守這一準(zhǔn)則

// 增加 Redis 對(duì)象引用 void incrRefCount(robj *o) {o->refcount++; }// 減少 Redis 對(duì)象引用。特別的,引用為零的時(shí)候會(huì)銷毀對(duì)象 void decrRefCount(robj *o) {if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");// 如果取消的是最后一個(gè)引用,則釋放資源if (o->refcount == 1) {// 不同數(shù)據(jù)類型,銷毀操作不同switch(o->type) {case REDIS_STRING: freeStringObject(o); break;case REDIS_LIST: freeListObject(o); break;case REDIS_SET: freeSetObject(o); break;case REDIS_ZSET: freeZsetObject(o); break;case REDIS_HASH: freeHashObject(o); break;default: redisPanic("Unknown object type"); break;}zfree(o);} else {o->refcount--;} }

得益于 Redis 是單進(jìn)程單線程工作的,所以增加/減少引用的操作不必保證原子性,這在 memcache 中是做不到的(memcached 是多線程的工作模式,需要做到互斥)

1、Keys?

redis是一個(gè)key-value db,首先key也是字符串類型,但是key中不能包括邊界字符,由于key不是binary safe的字符串,所以像”my key”和”mykey\n”這樣包含空格和換行的key是不允許的 ,順便說一下在redis內(nèi)部并不限制使用binary字符,這是redis協(xié)議限制的,”\r\n”在協(xié)議格式中會(huì)作為特殊字符。 redis 1.2以后的協(xié)議中部分命令已經(jīng)開始使用新的協(xié)議格式了(比如MSET),總之目前還是把包含邊界字符當(dāng)成非法的key,另外關(guān)于key的一個(gè)格式約定介紹下,object-type:id:field。比如user:1000:password,blog:xxidxx:title??

2、string

string是redis最基本的類型,而且string類型是二進(jìn)制安全的。意思是redis的string可以包含任何數(shù)據(jù),比如jpg圖片或者序列化的對(duì)象。從內(nèi)部實(shí)現(xiàn)來看其實(shí)string可以看作byte數(shù)組,最大上限是1G字節(jié)。?

struct sdshdr {long len;long free;char buf[]; };

buf是個(gè)char數(shù)組用于存貯實(shí)際的字符串內(nèi)容。其實(shí)char和c#中的byte是等價(jià)的,都是一個(gè)字節(jié) ,len是buf數(shù)組的長(zhǎng)度,free是數(shù)組中剩余可用字節(jié)數(shù)。?由此可以理解為什么string類型是二進(jìn)制安全的了。因?yàn)樗举|(zhì)上就是個(gè)byte數(shù)組。當(dāng)然可以包含任何數(shù)據(jù)了。?另外string類型可以被部分命令按int處理,比如incr等命令,redis的其他類型像list,set,sorted set ,hash它們包含的元素與都只能是string類型。?

編碼

字符串對(duì)象的編碼可以是 INT、RAW 或 EMBSTR。如果保存的是整數(shù)值并且可以用long表示,那么編碼會(huì)設(shè)置為INT。當(dāng)字符串值得長(zhǎng)度大于44字節(jié)使用RAW,小于等于44字節(jié)使用EMBSTR。

Redis在3.0引入EMBSTR編碼,這是一種專門用于保存短字符串的一種優(yōu)化編碼方式,這種編碼和RAW編碼都是用sdshdr簡(jiǎn)單動(dòng)態(tài)字符串結(jié)構(gòu)來表示。RAW編碼會(huì)調(diào)用兩次內(nèi)存分配函數(shù)來分別創(chuàng)建redisObject和sdshdr結(jié)構(gòu),而EMBSTR只調(diào)用一次內(nèi)存分配函數(shù)來分配一塊連續(xù)的空間保存數(shù)據(jù),比起RAW編碼的字符串更能節(jié)省內(nèi)存,以及能提升獲取數(shù)據(jù)的速度。

不過要注意!EMBSTR是不可修改的,當(dāng)對(duì)EMBSTR編碼的字符串執(zhí)行任何修改命令,總會(huì)先將其轉(zhuǎn)換成RAW編碼再進(jìn)行修改;而INT編碼在條件滿足的情況下也會(huì)被轉(zhuǎn)換成RAW編碼。

兩種字符串對(duì)象編碼方式的區(qū)別

/* Create a string object with EMBSTR encoding if it is smaller than* REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is* used.** The current limit of 39 is chosen so that the biggest string object* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. *///sdshdr8的大小為3個(gè)字節(jié),加上1個(gè)結(jié)束符共4個(gè)字節(jié) //redisObject的大小為16個(gè)字節(jié) //redis使用jemalloc內(nèi)存分配器,且jemalloc會(huì)分配8,16,32,64等字節(jié)的內(nèi)存 //一個(gè)embstr固定的大小為16+3+1 = 20個(gè)字節(jié),因此一個(gè)最大的embstr字符串為64-20 = 44字節(jié) #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44// 創(chuàng)建字符串對(duì)象,根據(jù)長(zhǎng)度使用不同的編碼類型 // createRawStringObject和createEmbeddedStringObject的區(qū)別是: // createRawStringObject是當(dāng)字符串長(zhǎng)度大于44字節(jié)時(shí),robj結(jié)構(gòu)和sdshdr結(jié)構(gòu)在內(nèi)存上是分開的 // createEmbeddedStringObject是當(dāng)字符串長(zhǎng)度小于等于44字節(jié)時(shí),robj結(jié)構(gòu)和sdshdr結(jié)構(gòu)在內(nèi)存上是連續(xù)的 robj *createStringObject(const char *ptr, size_t len) {if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)return createEmbeddedStringObject(ptr,len);elsereturn createRawStringObject(ptr,len); }

字符串對(duì)象編碼的優(yōu)化

/* Try to encode a string object in order to save space */ //嘗試優(yōu)化字符串對(duì)象的編碼方式以節(jié)約空間 robj *tryObjectEncoding(robj *o) {long value;sds s = o->ptr;size_t len;/* Make sure this is a string object, the only type we encode* in this function. Other types use encoded memory efficient* representations but are handled by the commands implementing* the type. */serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);/* We try some specialized encoding only for objects that are* RAW or EMBSTR encoded, in other words objects that are still* in represented by an actually array of chars. *///如果字符串對(duì)象的編碼類型為RAW或EMBSTR時(shí),才對(duì)其重新編碼if (!sdsEncodedObject(o)) return o;/* It's not safe to encode shared objects: shared objects can be shared* everywhere in the "object space" of Redis and may end in places where* they are not handled. We handle them only as values in the keyspace. *///如果refcount大于1,則說明對(duì)象的ptr指向的值是共享的,不對(duì)共享對(duì)象進(jìn)行編碼if (o->refcount > 1) return o;/* Check if we can represent this string as a long integer.* Note that we are sure that a string larger than 20 chars is not* representable as a 32 nor 64 bit integer. */len = sdslen(s); //獲得字符串s的長(zhǎng)度//如果len小于等于20,表示符合long long可以表示的范圍,且可以轉(zhuǎn)換為long類型的字符串進(jìn)行編碼if (len <= 20 && string2l(s,len,&value)) {/* This object is encodable as a long. Try to use a shared object.* Note that we avoid using shared integers when maxmemory is used* because every object needs to have a private LRU field for the LRU* algorithm to work well. */if ((server.maxmemory == 0 ||(server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) &&value >= 0 &&value < OBJ_SHARED_INTEGERS) //如果value處于共享整數(shù)的范圍內(nèi){decrRefCount(o); //原對(duì)象的引用計(jì)數(shù)減1,釋放對(duì)象incrRefCount(shared.integers[value]); //增加共享對(duì)象的引用計(jì)數(shù)return shared.integers[value]; //返回一個(gè)編碼為整數(shù)的字符串對(duì)象} else { //如果不處于共享整數(shù)的范圍if (o->encoding == OBJ_ENCODING_RAW) sdsfree(o->ptr); //釋放編碼為OBJ_ENCODING_RAW的對(duì)象o->encoding = OBJ_ENCODING_INT; //轉(zhuǎn)換為OBJ_ENCODING_INT編碼o->ptr = (void*) value; //指針ptr指向value對(duì)象return o;}}/* If the string is small and is still RAW encoded,* try the EMBSTR encoding which is more efficient.* In this representation the object and the SDS string are allocated* in the same chunk of memory to save space and cache misses. *///如果len小于44,44是最大的編碼為EMBSTR類型的字符串對(duì)象長(zhǎng)度if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {robj *emb;if (o->encoding == OBJ_ENCODING_EMBSTR) return o; //將RAW對(duì)象轉(zhuǎn)換為OBJ_ENCODING_EMBSTR編碼類型emb = createEmbeddedStringObject(s,sdslen(s)); //創(chuàng)建一個(gè)編碼類型為OBJ_ENCODING_EMBSTR的字符串對(duì)象decrRefCount(o); //釋放之前的對(duì)象return emb;}/* We can't encode the object...** Do the last try, and at least optimize the SDS string inside* the string object to require little space, in case there* is more than 10% of free space at the end of the SDS string.** We do that only for relatively large strings as this branch* is only entered if the length of the string is greater than* OBJ_ENCODING_EMBSTR_SIZE_LIMIT. *///無法進(jìn)行編碼,但是如果s的未使用的空間大于使用空間的10分之1if (o->encoding == OBJ_ENCODING_RAW &&sdsavail(s) > len/10){o->ptr = sdsRemoveFreeSpace(o->ptr); //釋放所有的未使用空間}/* Return the original object. */return o; }

3、list

list類型其實(shí)就是一個(gè)每個(gè)子元素都是string類型的雙向鏈表。所以[lr]push和[lr]pop命令的算法時(shí)間復(fù)雜度都是O(n),另外list會(huì)記錄鏈表的長(zhǎng)度。所以llen操作也是O(n).鏈表的最大長(zhǎng)度是(2的32次方-1)。?

我們可以通過push,pop操作從鏈表的頭部或者尾部添加刪除元素。這使得list既可以用作棧,也可以用作隊(duì)列。?有意思的是list的pop操作還有阻塞版本的。當(dāng)我們[lr]pop一個(gè)list對(duì)象,如果list是空,或者不存在,會(huì)立即返回nil。但是阻塞版本的b[lr]pop可以則可以阻塞,?當(dāng)然可以加超時(shí)時(shí)間,超時(shí)后也會(huì)返回nil。為什么要阻塞版本的pop呢,主要是為了避免輪詢。 如果我們用list來實(shí)現(xiàn)一個(gè)工作隊(duì)列。執(zhí)行任務(wù)的thread可以調(diào)用阻塞版本的pop去 ,獲取任務(wù)這樣就可以避免輪詢?nèi)z查是否有任務(wù)存在。當(dāng)任務(wù)來時(shí)候工作線程可以立即返回,也可以避免輪詢帶來的延遲。

編碼

Redis3.0之前的列表對(duì)象的編碼可以是ziplist或者linkedlist。當(dāng)列表對(duì)象保存的字符串元素的長(zhǎng)度都小于64字節(jié)并且保存的元素?cái)?shù)量小于512個(gè),使用ziplist編碼,可以通過修改配置list-max-ziplist-value和list-max-ziplist-entries來修改這兩個(gè)條件的上限值、兩個(gè)條件任意一個(gè)不滿足時(shí),ziplist會(huì)變?yōu)閘inkedlist。

從3.2開始Redis只使用quicklist作為列表的編碼,quicklist是ziplist和雙向鏈表的結(jié)合體,quicklist的每個(gè)節(jié)點(diǎn)都是一個(gè)ziplist。可以通過修改list-max-ziplist-size來設(shè)置一個(gè)quicklist節(jié)點(diǎn)上的ziplist的長(zhǎng)度,取正值表示通過元素?cái)?shù)量來限定ziplist的長(zhǎng)度;負(fù)數(shù)表示按照占用字節(jié)數(shù)來限定,并且Redis規(guī)定只能取-1到-5這五個(gè)負(fù)值

-5: 每個(gè)quicklist節(jié)點(diǎn)上的ziplist大小不能超過64 Kb。(注:1kb => 1024 bytes) -4: 每個(gè)quicklist節(jié)點(diǎn)上的ziplist大小不能超過32 Kb。 -3: 每個(gè)quicklist節(jié)點(diǎn)上的ziplist大小不能超過16 Kb。 -2: 每個(gè)quicklist節(jié)點(diǎn)上的ziplist大小不能超過8 Kb。(默認(rèn)值) -1: 每個(gè)quicklist節(jié)點(diǎn)上的ziplist大小不能超過4 Kb。

另外配置參數(shù)list-compress-depth表示一個(gè)quicklist兩端不被壓縮的節(jié)點(diǎn)個(gè)數(shù)

0: 表示都不壓縮。默認(rèn)值。 1: 表示quicklist兩端各有1個(gè)節(jié)點(diǎn)不壓縮,中間的節(jié)點(diǎn)壓縮。 2: 表示quicklist兩端各有2個(gè)節(jié)點(diǎn)不壓縮,中間的節(jié)點(diǎn)壓縮。 3: 表示quicklist兩端各有3個(gè)節(jié)點(diǎn)不壓縮,中間的節(jié)點(diǎn)壓縮。 依此類推…

這里采用的是一種叫LZF的無損壓縮算法

4、hash

哈希對(duì)象的編碼可以是ziplist或者h(yuǎn)ashtable。使用ziplist 編碼時(shí),保存同一鍵值對(duì)的兩個(gè)節(jié)點(diǎn)總是緊挨在一起,鍵節(jié)點(diǎn)在前,值節(jié)點(diǎn)在后,同時(shí)滿足以下兩個(gè)條件將使用ziplist編碼:

  • 所有鍵和值的字符串長(zhǎng)度小于64字節(jié)

  • 鍵值對(duì)的數(shù)量小于512個(gè)

不能滿足這兩個(gè)條件的都需要使用hashtable編碼。以上兩個(gè)上限值可以通過hash-max-ziplist-value和hash-max-ziplist-entries來修改

hash是一個(gè)string類型的field和value的映射表,它的添加,刪除操作都是O(1),hash特別適合用于存儲(chǔ)對(duì)象。 相較于將對(duì)象的每個(gè)字段存成單個(gè)string類型,將一個(gè)對(duì)象存儲(chǔ)在hash類型中會(huì)占用更少的內(nèi)存,并且可以更方便的存取整個(gè)對(duì)象。

省內(nèi)存的原因是新建一個(gè)hash對(duì)象時(shí)開始是用zipmap(又稱為small hash)來存儲(chǔ)的。?這個(gè)zipmap其實(shí)并不是hash table,但是zipmap相比正常的hash實(shí)現(xiàn)可以節(jié)省不少hash本身需要的一些元數(shù)據(jù)存儲(chǔ)開銷。?盡管zipmap的添加,刪除,查找都是O(n),但是由于一般對(duì)象的field數(shù)量都不太多。?所以使用zipmap也是很快的,也就是說添加刪除平均還是O(1)。?如果field或者value的大小超出一定限制后,redis會(huì)在內(nèi)部自動(dòng)將zipmap替換成正常的hash實(shí)現(xiàn),這個(gè)限制可以在配置文件中指定?

hash-max-zipmap-entries 64 #配置字段最多64個(gè) hash-max-zipmap-value 512 #配置value最大為512字節(jié)

5、set

集合對(duì)象的編碼可以是intset或者h(yuǎn)ashtable。當(dāng)滿足以下兩個(gè)條件時(shí)使用intset編碼:

  • 所有元素都是整數(shù)值

  • 元素?cái)?shù)量不超過512個(gè)

可以修改set-max-intset-entries設(shè)置元素?cái)?shù)量的上限。使用hashtable編碼時(shí),字典的每一個(gè)鍵都是字符串對(duì)象,每個(gè)字符串對(duì)象包含一個(gè)集合元素,字典的值全部設(shè)置為null。

redis的set是string類型的無序集合。set元素最大可以包含(2的32次方-1)個(gè)元素。?set的是通過hash table實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是O(1)。hash table會(huì)隨著添加或者刪除自動(dòng)的調(diào)整大小。?需要注意的是調(diào)整hash table大小時(shí)候需要同步(獲取寫鎖)會(huì)阻塞其他讀寫操作。?可能不久后就會(huì)改用跳表(skip list)來實(shí)現(xiàn)跳表已經(jīng)在sorted set中使用了 關(guān)于set集合類型除了基本的添加刪除操作,其他有用的操作還包含集合的取并集(union),交集(intersection),?差集(difference)。

6、sorted set

有序集合對(duì)象的編碼可以是ziplist或者skiplist。同時(shí)滿足以下條件時(shí)使用ziplist編碼:

  • 元素?cái)?shù)量小于128個(gè)

  • 所有member的長(zhǎng)度都小于64字節(jié)

以上兩個(gè)條件的上限值可通過zset-max-ziplist-entries和zset-max-ziplist-value來修改。

ziplist編碼的有序集合使用緊挨在一起的壓縮列表節(jié)點(diǎn)來保存,第一個(gè)節(jié)點(diǎn)保存member,第二個(gè)保存score。ziplist內(nèi)的集合元素按score從小到大排序,score較小的排在表頭位置。

skiplist編碼的有序集合底層是一個(gè)命名為zset的結(jié)構(gòu)體,而一個(gè)zset結(jié)構(gòu)同時(shí)包含一個(gè)字典和一個(gè)跳躍表。跳躍表按score從小到大保存所有集合元素。而字典則保存著從member到score的映射,這樣就可以用O(1)的復(fù)雜度來查找member對(duì)應(yīng)的score值。雖然同時(shí)使用兩種結(jié)構(gòu),但它們會(huì)通過指針來共享相同元素的member和score,因此不會(huì)浪費(fèi)額外的內(nèi)存。

和set一樣sorted set也是string類型元素的集合,不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類型的score。sorted set的實(shí)現(xiàn)是skip list和hash table的混合體,當(dāng)元素被添加到集合中時(shí),一個(gè)元素到score的映射被添加到hash table中,所以給定一個(gè)元素獲取score的開銷是O(1),另一個(gè)score到元素的映射被添加到skip list并按照score排序,所以就可以有序的獲取集合中的元素。?添加,刪除操作開銷都是O(1)和skip list的開銷一致,redis的skip list實(shí)現(xiàn)用的是雙向鏈表,這樣就可以逆序從尾部取元素。?sorted set最經(jīng)常的使用方式應(yīng)該是作為索引來使用,我們可以把要排序的字段作為score存儲(chǔ),對(duì)象的id當(dāng)元素存儲(chǔ)。

?參考:

http://weixin.niurenqushi.com/article/2017-05-07/4842721.html

https://mp.weixin.qq.com/s?__biz=MzA5ODM5MDU3MA==&mid=2650862682&idx=1&sn=41ea245ac0a9dbfc943dd1d03228a14e&chksm=8b66131fbc119a09ec27e70dca884425c5c1b54deca2f1fde471b54fe723a23b1b347752b7f8&scene=21#wechat_redirect

https://mp.weixin.qq.com/s?__biz=MzA5ODM5MDU3MA==&mid=2650862680&idx=1&sn=978a6ea4971b6b98f266fa34bf1b49d8&chksm=8b66131dbc119a0bc82165c67dd6b13c1621b7ea289da6dd23e93adc6a86c43790c41d47da13&scene=21#wechat_redirect

http://zhangtielei.com/posts/blog-redis-dict.html

http://crazyjvm.iteye.com/blog/1720289

http://blog.csdn.net/men_wen/article/details/70257207

轉(zhuǎn)載于:https://www.cnblogs.com/chenpingzhao/p/6965164.html

總結(jié)

以上是生活随笔為你收集整理的redis内部数据结构深入浅出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。