Redis並無直接使用內部的基本數據結構來實現鍵值對數據庫,而是基於這些數據結構建立了一個對象系統,這個系統包含字符串對象、列表對象、哈希對象、集合對象和有序集合對象這五種類型的對象, 每種對象都用到了至少一種內部的基本數據結構。
一.對象的結構node
typedef struct redisObject { unsigned type;// 類型 unsigned encoding;// 編碼 void *ptr;// 指向底層實現數據結構的指針 int refcount; // 引用計數 unsigned lru; //該屬性記錄了對象最後一次被命令程序訪問的時間 } robj;
(1).type屬性redis
類型常量 | 對象的名稱 |
---|---|
REDIS_STRING | 字符串對象 |
REDIS_LIST | 列表對象 |
REDIS_HASH | 哈希對象 |
REDIS_SET | 集合對象 |
REDIS_ZSET | 有序集合對象 |
(2).encoding屬性數據庫
編碼常量 | 編碼所對應的底層數據結構 |
---|---|
REDIS_ENCODING_INT | long 類型的整數 |
REDIS_ENCODING_EMBSTR | embstr 編碼的簡單動態字符串 |
REDIS_ENCODING_RAW | 簡單動態字符串 |
REDIS_ENCODING_HT | 字典 |
REDIS_ENCODING_LINKEDLIST | 鏈表 |
REDIS_ENCODING_ZIPLIST | 壓縮列表 |
REDIS_ENCODING_INTSET | 整數集合 |
REDIS_ENCODING_SKIPLIST | 跳躍表 |
(3).refcount服務器
內存回收:由於C語言並不具有自動的內存回收功能,因此Redis在本身的對象系統中構建了一個引用計數(reference counting)技術實現的內存回收機制,經過這一機制,程序能夠經過跟蹤對象的引用計數信息,在適當的時候自動釋放對象並進行內存回收。
a.在建立一個新對象時, 引用計數的值會被初始化爲 1 ; b.當對象被一個新程序使用時, 它的引用計數值會被增一; c.當對象再也不被一個程序使用時, 它的引用計數值會被減一; d.當對象的引用計數值變爲 0 時, 對象所佔用的內存會被釋放。
對象共享:經過引用技術無需對相同對象從新分配內存,而是直接經過對象共享相同的內存塊,共享對象機制對於節約內存很是有幫助, 數據庫中保存的相同值對象越多, 對象共享機制就能節約越多的內存。
目前來講,Redis會在初始化服務器時,建立一萬個字符串對象,這些對象包含了從0到9999的全部整數值,當服務器須要用到值爲0到9999的字符串對象時,服務器就會使用這些共享對象,而不是新建立對象。
a.將數據庫鍵的值指針指向一個現有的值對象; b.將被共享的值對象的引用計數增一。
Redis只對包含整數值的字符串對象進行共享。
(3).lru數據結構
空轉時長就是經過將當前時間減去鍵的值對象的 lru 時間計算得出;能夠用於回收內存(當開啓maxmemory選項時),空轉時長較高的那部分鍵會優先被服務器釋放
volatile-lru:從已設置過時時間的數據集中挑選最近最少使用的數據淘汰 volatile-ttl:從已設置過時時間的數據集中挑選將要過時的數據淘汰 volatile-random:從已設置過時時間的數據集中任意選擇數據淘汰 allkeys-lru:從數據集中挑選最近最少使用的數據淘汰 allkeys-random:從數據集中任意選擇數據淘汰 no-enviction(驅逐):禁止驅逐數據,只是發出警告
二.字符串對象(REDIS_STRING)dom
a.若是一個字符串對象保存的是整數值,而且這個整數值能夠用long類型來表示,那麼字符串對象會將整數值保存在字 符串對象結構的 ptr屬性裏面(將 void* 轉換成 long ),並將字符串對象的編碼設置爲 int b.若是字符串對象保存的是一個字符串值,而且這個字符串值的長度大於39字節,那麼字符串對象將使用一個簡單動態 字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲 raw c.若是字符串對象保存的是一個字符串值,而且這個字符串值的長度小於等於39字節,那麼字符串對象將使用 embstr 編碼的方式來保存這個字符串值。(embstr能夠連續分配內存)
(1).編碼的轉換編碼
a.對於int編碼的字符串對象來講,若是咱們向對象執行了一些命令,使得這個對象保存的再也不是整數值,而是一個字 符串值,那麼字符串對象的編碼將從int變爲raw b.由於Redis沒有爲embstr編碼的字符串對象編寫任何相應的修改程序 (只有 int 編碼的字符串對象和 raw 編碼的字 符串對象有這些程序),因此 embstr 編碼的字符串對象其實是隻讀的:當咱們對 embstr 編碼的字符串對象執行任 何修改命令時,程序會先將對象的編碼從 embstr 轉換成 raw ,而後再執行修改命令; 由於這個緣由,embstr 編碼 的字符串對象在執行修改命令以後,總會變成一個 raw 編碼的字符串對象。
三.列表對象(REDIS_LIST)spa
a.當一個列表對象只包含少許列表項,而且每一個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼Redis 就會使用壓縮列表來作列表鍵的底層實現。 b.當一個列表對象包含較多的列表項,那麼Redis就會使用鏈表結構做爲底層實現, 每一個雙端鏈表節點(node)都保存 了一個字符串對象, 而每一個字符串對象都保存了一個列表元素。
(1).編碼轉換指針
a.當列表對象能夠同時知足如下兩個條件時, 列表對象使用 ziplist 編碼: 列表對象保存的全部字符串元素的長度都小於 64 字節; 列表對象保存的元素數量小於 512 個; b.不能知足這兩個條件的列表對象須要使用 linkedlist 編碼。
四.哈希對象(REDIS_HASH)code
a.當一個哈希對象只包含少許鍵值對,而且每一個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字符串,那 麼 Redis 就會使用壓縮列表來作哈希鍵的底層實現。 b.當一個哈希對象包含較多鍵值對,哈希對象使用字典做爲底層實現
(1).編碼轉換
a.當哈希對象能夠同時知足如下兩個條件時, 哈希對象使用 ziplist 編碼: 哈希對象保存的全部鍵值對的鍵和值的字符串長度都小於 64 字節; 哈希對象保存的鍵值對數量小於 512 個; b.不能知足這兩個條件的哈希對象須要使用 hashtable 編碼。
五.集合對象(REDIS_SET)
a.當一個集合對象只包含整數值元素, 而且這個集合對象的元素數量很少時, Redis就會使用整數集合做爲集合對象 的底層實現。 b.當一個集合對象包含非整數值元素,或者元素數量較多時,Redis就會使用字典做爲底層實現
(1).編碼的轉換
a.當集合對象能夠同時知足如下兩個條件時, 對象使用 intset 編碼: 集合對象保存的全部元素都是整數值; 集合對象保存的元素數量不超過 512 個; b.不能知足這兩個條件的集合對象須要使用 hashtable 編碼。
六.有序集合對象(REDIS_ZSET)
a.當一個有序集合對象只包含少許列表項,而且每一個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼 Redis就會使用壓縮列表來作有序集合的底層實現,壓縮列表內的集合元素按分值從小到大進行排序, 分值較小的元素 被放置在靠近表頭的方向, 而分值較大的元素則被放置在靠近表尾的方向。 b.當一個集合對象包含較多元素,Redis就會使用跳躍表與字典做爲底層實現。dict字典爲有序集合建立了一個從成員 到分值的映射,經過這個字典, 程序能夠用 O(1) 複雜度查找給定成員的分值。zsl跳躍表按分值從小到大保存了全部 集合元素,跳躍表節點的 object 屬性保存了元素的成員, 而跳躍表節點的 score 屬性則保存了元素的分值。 經過 這個跳躍表, 程序能夠對有序集合進行範圍型操做,
(1).編碼的轉換
a.當有序集合對象能夠同時知足如下兩個條件時, 對象使用 ziplist 編碼: 有序集合保存的元素數量小於 128 個; 有序集合保存的全部元素成員的長度都小於 64 字節; b.不能知足以上兩個條件的有序集合對象將使用 skiplist 編碼。