(Redis設計與實現-2) 對象

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 編碼。
相關文章
相關標籤/搜索