Redis目前基本的數(shù)據(jù)類型有String、List蜈首、Set岗憋、ZSet肃晚、Hash五種,首先Redis是C語言開發(fā)的仔戈,所以底層就是用C語言封裝數(shù)據(jù)結構或者C語言本身提供的數(shù)據(jù)結構來存儲关串。redis內(nèi)部的主要數(shù)據(jù)結構主要有 簡單字符串(SDS)、雙端鏈表监徘、字典晋修、壓縮列表、跳躍表凰盔、整數(shù)集合 墓卦。Redis內(nèi)部 并沒有直接使用這些數(shù)據(jù)結構 來實現(xiàn)鍵值對數(shù)據(jù)庫,而是基于這些數(shù)據(jù)結構創(chuàng)建了一個對象系統(tǒng)户敬,這個 對象系統(tǒng) 包含了我們所熟知的五種基本類型數(shù)據(jù)落剪,也就是 字符串對象睁本、列表對象、哈希對象忠怖、集合對象和有序集合對象 這五種類型的對象呢堰。而它們每一種對象都使用到了至少一種前面所介紹的數(shù)據(jù)結構。下面介紹一下redis內(nèi)部的主要幾個數(shù)據(jù)結構 簡單字符串(SDS)脑又、雙端鏈表暮胧、壓縮列表、跳躍表 的定義问麸。然后再介紹一下redis基本的五種數(shù)據(jù)類型往衷,也就是五種類型的對象用到了上面的哪些數(shù)據(jù)結構。
redis的數(shù)據(jù)結構
SDS(Simple Dynamic String)簡單字符串
1严卖、redis定義:
2席舍、使用范圍:在redis里面,C本身的字符串只會作為字符串字面量(String literal)只用在一些不必對字符串值修改的地方哮笆,比如打印日志来颤。
而redis需要使用字符串存儲并且會修改的地方,都使用了SDS來存儲稠肘。例如Key值福铅。
3、優(yōu)點:使用SDS來存儲字符串的優(yōu)點:
- SDS的len屬性直接記錄了長度项阴,獲取字符串長度的復雜度為O(1)滑黔。
- C字符串本身不記錄長度容易產(chǎn)生緩存區(qū)溢出,而SDS杜絕了緩沖區(qū)的溢出环揽。
- C字符串本身不記錄長度略荡,每次修改都要重新分配內(nèi)存,SDS減少了重新分配內(nèi)存次數(shù)歉胶。
- 優(yōu)化了字符串縮短操作汛兜。并且可以保存任意格式的二進制數(shù)據(jù),而C字符串必須含有編碼通今。
鏈表(list)
1粥谬、鏈表:listNode結構來保存,多個listNode可以形成雙向鏈表辫塌,redis定義了list表示頭節(jié)點來持有鏈表帝嗡,下圖分別是節(jié)點listNode和鏈表list的定義。
2璃氢、redis定義:
- 節(jié)點listNode
- 鏈表list
3、使用范圍:鏈表在redis中用作了列表鍵狮辽、發(fā)布與訂閱一也、慢查詢巢寡、監(jiān)視器等
跳躍表(zskiplist)
1、跳躍表:是一種有序得數(shù)據(jù)結構椰苟,通過在每個節(jié)點上維持多個指向其他節(jié)點的指針抑月,從而達到快速訪問的目的,可以理解為改進版的雙端鏈表舆蝴,改進的手段是通過空間換取了時間谦絮。
2、復雜度:跳躍表支持平均O(logN)洁仗、最壞O(N)的查找復雜度层皱,大部分條件下,跳躍表的效率可以和平衡樹媲美赠潦,并且實現(xiàn)比平衡樹簡單叫胖。
- 跳躍表節(jié)點zskiplistNode
- 跳躍表zskiplist
3、跳躍表結構圖:
仔細觀察上圖跳躍表的結構后她奥,發(fā)現(xiàn)如果節(jié)點的層數(shù)越高瓮增,那么這個節(jié)點訪問其他節(jié)點的速度就越快。換言之哩俭,level越高绷跑,代表了這個跳躍表的查找效率可能會比較高。當然并不是絕對的凡资,因為redis每次創(chuàng)建跳躍表節(jié)點時砸捏,程序是根據(jù)冪次定律(越大的數(shù)出現(xiàn)概率越小), 生成層數(shù)高度讳苦。同時带膜,節(jié)點的順序是根據(jù)每個節(jié)點的分值排序的,如果分值相同鸳谜,那么根據(jù)對象的大小排序膝藕。
壓縮列表(ziplist)
1、壓縮列表:是redis為了節(jié)省內(nèi)存而開發(fā)的咐扭,是由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型數(shù)據(jù)結構芭挽,一個壓縮列表的可以包含多個節(jié)點,每個節(jié)點可以保存一個字節(jié)數(shù)組或者一個整數(shù)值蝗肪。
2袜爪、壓縮列表結構圖:
3、壓縮列表特點:
- 是一種為節(jié)省內(nèi)存開發(fā)的順序性數(shù)據(jù)結構
- 可以包含多個節(jié)點薛闪,每個節(jié)點保存一個字節(jié)數(shù)組或者整數(shù)值
- 添加新節(jié)點到壓縮列表辛馆,或者從壓縮列表刪除節(jié)點,可能會引發(fā)連鎖更新操作,但是機率不高
Redis五種基本數(shù)據(jù)類型
上面提到過昙篙,redis并沒有使用上面的數(shù)據(jù)結構直接用來實現(xiàn)鍵值對數(shù)據(jù)庫腊状,也就是常說的五種基本數(shù)據(jù)類型,而是創(chuàng)建了一個對象系統(tǒng)苔可,這個系統(tǒng)包含了字符串對象缴挖,列表對象、哈希對象焚辅、集合對象映屋、和有序集合對象這五種基本數(shù)據(jù)類型。這樣做有一個好處是同蜻,我們可以針對不同的場景棚点,對相同的數(shù)據(jù)類型對象使用不同的數(shù)據(jù)結構,來優(yōu)化提高效率埃仪。
redisObject對象
1乙濒、對象:redis的鍵值對都是一個redisObject結構,該結構中有三個屬性卵蛉,type類型屬性颁股、encoding編碼屬性、ptr指向底層數(shù)據(jù)結構屬性傻丝。
- redisObject對象定義
- 數(shù)據(jù)庫的key值都是一個string字符串對象
2甘有、編碼常量:
String類型
字符串對象的編碼是 int、raw葡缰、embstr 亏掀。參考上面的編碼常量表,也就是說字符串類型的數(shù)據(jù)底層的數(shù)據(jù)結構使用的是整數(shù)泛释、SDS滤愕、embstr編碼的SDS。
1怜校、編碼轉換
即上述幾種編碼會在何時轉換间影,也就是redis底層決定用什么存儲字符串數(shù)據(jù)?茄茁。
當int類型的編碼通過操作存儲的是字符串值魂贬,那么字符串對象的編碼將從int變?yōu)閞aw。
List類型
列表對象的編碼可以是 zipList壓縮列表 和 linkedlist雙端鏈表 裙顽。
1付燥、編碼轉換
即上述兩種編碼會在何時轉換,也就是redis底層什么時候會用壓縮列表存儲列表數(shù)據(jù)愈犹?什么時候會使用雙端鏈表存儲列表數(shù)據(jù)键科。
當列表同時滿足以下兩個條件時,列表對象會使用zipList編碼,也就是壓縮列表
- 列表對象保存的所有字符串元素的長度都小于64字節(jié)
- 列表保存的元素少于512個萝嘁,
2梆掸、配置
上述兩個條件是支持配置的,也就是說我們可以redis直接讀取我們的配置牙言,來決定列表list類型底層使用什么樣的數(shù)據(jù)結構來存儲數(shù)據(jù)
- list-max-ziplist-value
- list-max-ziplist-entries
Set類型
集合對象使用的是 intset整數(shù)集合 (intset底層使用的是整數(shù)集合數(shù)據(jù)結構)或者 hashtable哈希表 (hashtable底層使用的是字典數(shù)據(jù)結構,我們并沒有在本文做詳細介紹怪得,有需要可以自己了解)
1咱枉、編碼轉換
當集合對象同時滿足下面兩個條件,會使用intset編碼
- 集合對象保存的所有對象都是整數(shù)值
- 集合對象保存的元素數(shù)量小于512個徒恋;
2蚕断、配置
上述第二個條件是支持配置的。
- set-max-intset-entries
ZSet類型
有序集合的編碼使用的是 ziplist壓縮列表 和 skiplist跳躍表 入挣。
注意:上面介紹skiplist的時候我們可以從結構圖中明顯看到存儲集合元素的時候亿乳,score在每個節(jié)點中式如何存儲的。那么如果ZSet使用的式ziplist壓縮列表径筏,redis怎么存儲score和value值呢葛假?其實很簡單,每個集合的元素都使用兩個節(jié)點來存儲滋恬,第一個節(jié)點保存的是成員(member)聊训,第二個元素保存的是元素的分值(score)
1、編碼轉換
當有序集合對象可以同時滿足以下兩個條件時恢氯,使用ziplist編碼
- 有序集合的所有元素長度都小于64字節(jié)
- 有序集合的元素數(shù)量小于128個带斑;
2、配置
上述兩個條件是支持配置的勋拟。
- zset-max-ziplist-value
- zset-max-ziplist-entries
Hash類型
哈希對象使用的是 ziplist壓縮列表 或 hashtable哈希表 勋磕。(hashtable底層使用的是字典數(shù)據(jù)結構,我們并沒有在本文做詳細介紹敢靡,有需要可以自己了解)
1挂滓、編碼轉換
當哈希對象同時滿足下面兩個條件,使用ziplist壓縮列表
- 哈希對象保存的所有鍵值對的鍵和值的字符串長度都小于64字節(jié)
- 哈希對象保存的鍵值對的數(shù)量小于512個醋安;
2杂彭、配置
上述兩個條件是支持配置的。
- hash-max-ziplist-value
- hash-max-ziplist-entries