? ? redis沒(méi)有直接使用數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)鍵值對(duì)的數(shù)據(jù)庫(kù)辫封,而是基于這些數(shù)據(jù)結(jié)構(gòu)創(chuàng)建了一個(gè)對(duì)象系統(tǒng)厉熟,包含字符串對(duì)象、列表對(duì)象溉潭、哈希對(duì)象、集合對(duì)象和有序集合對(duì)象五種類型少欺。
? ? 對(duì)redis數(shù)據(jù)庫(kù)鍵值對(duì)來(lái)說(shuō)喳瓣,鍵永遠(yuǎn)都是字符串對(duì)象,而值可以 是字符串對(duì)象赞别、列表對(duì)象畏陕、哈希對(duì)象、集合對(duì)象和有序集合對(duì)象五種類型仿滔,故接下來(lái)所說(shuō)的幾種對(duì)象惠毁,都是鍵值對(duì)的值對(duì)象。
type: 對(duì)象類型崎页,五種類型之一鞠绰。
encoding:對(duì)象所使用的編碼,也即對(duì)象使用了什么數(shù)據(jù)結(jié)構(gòu)作為底層實(shí)現(xiàn)飒焦。
每種類型的對(duì)象都至少使用了兩種不同編碼(數(shù)據(jù)結(jié)構(gòu))蜈膨。
字符串對(duì)象:
? ? ? ? 整數(shù)值屿笼、embstr、簡(jiǎn)單動(dòng)態(tài)字符串
列表對(duì)象:
? ? ? ? 壓縮列表翁巍、雙端列表
哈希對(duì)象:
? ? ? ? 壓縮列表驴一、字典實(shí)現(xiàn)
集合對(duì)象:
? ? ? ? 整數(shù)集合、字典實(shí)現(xiàn)
有序集合對(duì)象:
? ? ? ? 壓縮列表實(shí)現(xiàn)灶壶、跳躍表和字典實(shí)現(xiàn)
一 字符串對(duì)象
? ? 字符串對(duì)象保存的是整數(shù)值肝断,且可以用long表示,值會(huì)保存在pre屬性里驰凛,并將字符串對(duì)象的編碼設(shè)置為int胸懈。
? ? 字符串對(duì)象是唯一一種會(huì)被其他四種對(duì)象嵌套的對(duì)象。
? ? 字符串對(duì)象保存的是字符串值洒嗤,且值的長(zhǎng)度大于32字節(jié)箫荡,則以SDS來(lái)保存這個(gè)字符串值,并將對(duì)象編碼設(shè)置為raw渔隶。
? ???字符串對(duì)象保存的是字符串值羔挡,且值的長(zhǎng)度小于等于32字節(jié),則以SDS來(lái)保存這個(gè)字符串值间唉,并將對(duì)象編碼設(shè)置為embstr绞灼。
? ? raw和embstr的區(qū)別在于,raw會(huì)調(diào)用兩次內(nèi)存分配來(lái)分別創(chuàng)建redisObject結(jié)構(gòu)和sdshdr結(jié)構(gòu)呈野,而embstr則只調(diào)用一次內(nèi)存分配函數(shù)來(lái)分配一塊連續(xù)的空間低矮。同理,釋放對(duì)象內(nèi)存的時(shí)候被冒,raw需要調(diào)用兩次军掂,而embstr只需調(diào)用一次。
? ? embstr編碼的字符串對(duì)象在執(zhí)行命令時(shí)昨悼,效果和raw編碼字符串對(duì)象效果一樣蝗锥。
? ? embstr編碼字符串對(duì)象只讀,一旦修改率触,則會(huì)變?yōu)閞aw編碼字符串终议。
二 列表對(duì)象
列表對(duì)象的編碼是ziplist或linkedlist。
ziplist編碼的列表對(duì)象使用壓縮列表作為底層實(shí)現(xiàn)葱蝗,每個(gè)壓縮列表節(jié)點(diǎn)保存了一個(gè)列表元素穴张。
linkedlist編碼的列表對(duì)象使用雙端鏈表作為底層實(shí)現(xiàn),每個(gè)雙端鏈表節(jié)點(diǎn)都保存了一個(gè)字符串對(duì)象两曼,而每個(gè)字符串對(duì)象都保存了一個(gè)列表元素皂甘。
為了簡(jiǎn)化字符串對(duì)象表示,實(shí)際StringObject的結(jié)構(gòu)如下圖:
列表對(duì)象在壓縮列表和雙端鏈表間的轉(zhuǎn)換:
1,列表對(duì)象保存的所有字符串元素的長(zhǎng)度都小于64字節(jié)悼凑。
2叮贩,列表對(duì)象保存的元素?cái)?shù)量小于512個(gè)击狮。
滿足上述兩個(gè)條件,列表對(duì)象使用ziplist編碼,否則使用linkedlist編碼。
注:以上兩個(gè)條件的上限可配置修改朦佩,list-max-ziplist-value 和 list-max-ziplist-entries 。
三 哈希對(duì)象
哈希對(duì)象的編碼可以是ziplist 或 hashtable 档冬。
ziplist編碼的哈希對(duì)象使用壓縮列表作為底層實(shí)現(xiàn),有新鍵值對(duì)(指值是鍵值對(duì)形式)進(jìn)入時(shí)桃纯,先把保存了鍵的壓縮列表節(jié)點(diǎn)放到壓縮列表表尾酷誓,然后再把保存了值的壓縮列表節(jié)點(diǎn)放到壓縮列表表尾,故同一鍵值對(duì)的兩個(gè)節(jié)點(diǎn)總是連在一起态坦。
hashtable編碼的哈希對(duì)象使用字典作為底層實(shí)現(xiàn)盐数,哈希對(duì)象中的每個(gè)鍵值對(duì)都使用一個(gè)字典鍵值對(duì)來(lái)保存。
哈希對(duì)象兩種編碼間的轉(zhuǎn)換:
1伞梯,哈希對(duì)象所保存的所有鍵值對(duì)的鍵和值的字符串長(zhǎng)度都小于64字節(jié)玫氢。
2,哈希對(duì)象的鍵值對(duì)的數(shù)量小于512個(gè)谜诫。
滿足上述兩個(gè)條件漾峡,哈希對(duì)象使用ziplist編碼,否則使用hashtable編碼喻旷。
注:以上兩個(gè)條件的上限可配置修改生逸,hash-max-ziplist-value 和 hash-max-ziplist-entries 。
四 集合對(duì)象
集合對(duì)象編碼可以用intset 或者 hashtable 且预。
intset編碼的集合對(duì)象使用整數(shù)集合作為底層實(shí)現(xiàn)槽袄,集合對(duì)象的所有元素都被保存在整數(shù)集合里。
hashtable編碼的集合對(duì)象使用字段作為底層實(shí)現(xiàn)锋谐,字典的每一個(gè)鍵都是字符串對(duì)象掰伸,每個(gè)字符串對(duì)象包含了一個(gè)集合元素,而字典的值全部被置為null 怀估。
集合對(duì)象兩種編碼間轉(zhuǎn)換:
1,集合對(duì)象保存的所有元素都是整數(shù)值 合搅。
2多搀,集合對(duì)象保存的元素個(gè)數(shù)不超過(guò)512 。
滿足上述條件灾部,則使用intset編碼康铭,否則,使用hashtable編碼 赌髓。
注:以上第二個(gè)條件的上限可配置修改从藤, set-max-intset-entries 催跪。
五 有序集合對(duì)象
有序集合的編碼可以用ziplist 或 skiplist 。
ziplist編碼的有序集合對(duì)象使用壓縮列表作為底層實(shí)現(xiàn)夷野,每個(gè)集合元素使用 兩個(gè)緊挨在一起的壓縮列表節(jié)點(diǎn)保存懊蒸,第一個(gè)節(jié)點(diǎn)保存元素成員(member),第二個(gè)節(jié)點(diǎn)保存元素的分值(score)悯搔。
壓縮列表內(nèi)的集合元素按分值從小到大排序骑丸,分值小的元素靠近表頭,分值大的靠近表尾妒貌。
skiplist 編碼的有序集合對(duì)象使用zset結(jié)構(gòu)作為底層實(shí)現(xiàn)通危,一個(gè)zset結(jié)構(gòu)同時(shí)包含一個(gè)字典和一個(gè)跳躍表。
zset結(jié)構(gòu)中的zs1跳躍表按分值從小到大保存所有集合元素灌曙,每個(gè)跳躍表節(jié)點(diǎn)都保存了一個(gè)集合元素菊碟,跳躍表節(jié)點(diǎn)的object屬性保存了元素成員,而跳躍表節(jié)點(diǎn)的score屬性則保存了元素的分值在刺。
zset結(jié)構(gòu)中的dict字典為有序集合創(chuàng)建了一個(gè)從成員到分值的映射逆害,字典匯中的每個(gè)鍵值對(duì)都保存了一個(gè)集合元素,字典的鍵保存了元素的成員增炭,字典的值保存了元素的分值忍燥。
理論上,有序集合可以單獨(dú)使用字典或跳躍表一種數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)隙姿,但無(wú)論單獨(dú)用哪種梅垄,性能上總是比不上同時(shí)使用。比如查找指定成員分值输玷,直接使用dict队丝,而查找成員排名,則使用跳躍表欲鹏。
有序集合ziplist和zset編碼間的轉(zhuǎn)換:
1机久,有序集合保存的元素?cái)?shù)量小于128個(gè)。
2赔嚎,有序集合保存的所有元素成員長(zhǎng)度小于64字節(jié)膘盖。
滿足上述兩個(gè)條件,則使用ziplist 尤误,否則侠畔,使用zset 。
注:以上兩個(gè)條件的上限可配置修改损晤,zset-max-ziplist-value 和 zset-max-ziplist-entries 软棺。
五 內(nèi)存收回
因C語(yǔ)言沒(méi)有自動(dòng)內(nèi)存收回功能,所以redis自己構(gòu)建了一個(gè)引用計(jì)數(shù)技術(shù)實(shí)現(xiàn)內(nèi)存回收機(jī)制尤勋。
1喘落,創(chuàng)建一個(gè)新對(duì)象時(shí)茵宪,引用計(jì)數(shù)的值被初始化為1;
2瘦棋,當(dāng)對(duì)象被一個(gè)新程序使用時(shí)稀火,它的引用計(jì)數(shù)增加1;
3兽狭,當(dāng)對(duì)象不再被一個(gè)程序使用時(shí)憾股,它的引用計(jì)數(shù)減1;
4箕慧,當(dāng)對(duì)象的引用計(jì)數(shù)值變?yōu)?時(shí)服球,對(duì)象所占用的內(nèi)存會(huì)被釋放。
六 對(duì)象共存
對(duì)象引用計(jì)數(shù)的屬性還帶有對(duì)象共存的作用颠焦。
redis中斩熊,多個(gè)鍵共享同一個(gè)值時(shí),數(shù)據(jù)庫(kù)鍵的值指針指向一個(gè)現(xiàn)有的值對(duì)象伐庭,同時(shí)被共享的值對(duì)象的引用計(jì)數(shù)增一粉渠。
目前來(lái)說(shuō),redis初始化服務(wù)器時(shí)圾另,會(huì)創(chuàng)建一萬(wàn)個(gè)字符串對(duì)象霸株,包含從0-9999所有整數(shù)值,所以當(dāng)用到0-9999的字符串對(duì)象時(shí)集乔,服務(wù)器會(huì)共享這些對(duì)象去件,而不會(huì)再創(chuàng)建新對(duì)象。
七 對(duì)象的空轉(zhuǎn)時(shí)長(zhǎng)
lru:記錄了對(duì)象最后一次被命令程序訪問(wèn)的時(shí)間扰路。
redisObject 完整結(jié)構(gòu):
參考文獻(xiàn)《redis設(shè)計(jì)與實(shí)現(xiàn)第二版》