redis筆記-對象系統篇

2018-1-4 by Atlas

* 簡述

  • redis並無直接使用SDS、鏈表、字典、壓縮列表、整數集合、跳躍表這些數據結構來實現鍵值對數據庫,而是基於這些數據結構建立了一個對象系統,這個系統包含字符串對象、列表對象、哈希對象、集合對象、有序集合對象這五種類型的對象,每種對象都至少用到了一種上面提到的數據結構。
  • 使用對象的一個好處是,能夠針對不一樣的使用場景,爲對象設置多種不一樣的數據結構實現,從而優化對象在不一樣場景下的使用效率。
  • 對象系統還實現了基於引用計數技術的內存回收機制,釋放再也不使用的對象所佔用的內存。
  • 引用計數技術實現了對象共享機制,經過多個數據庫鍵共享同一個對象來節約內存。

* 定義

typeof struct redisObject {
        // 類型
        unsigned type:4;
        // 編碼
        unsigned encoding:4;
        // 指向底層實現數據結構的指針
        void *ptr;
        // 引用計數
        int refcount;
        // 空轉時長
        unsigned lru:22;
} robj;

* 類型

對象的類型

TYPE命令,返回的結果爲數據庫鍵對應的值對象的類型,而不是鍵對象的類型。redis

redis> SET msg "hello"
ok
redis> TYPE msg
string

不一樣類型值對象的TYPE命令輸出

* 編碼

對象的編碼

OBJECT ENCODING命令查看數據庫鍵的值對象的編碼。算法

redis> SET msg "hello"
ok
redis> OBJECT ENCODING msg
"embstr"

不一樣類型和編碼的對象

OBJECT ENCODING對不一樣編碼的輸出

* 類型檢查

類型檢查是經過redisObject結構的type屬性來實現的:
1)執行命令前,服務器先檢查輸入數據庫鍵的值對象是否爲執行命令所需的類型,若是是,服務器就對鍵執行命令;
2)不然,服務器拒絕執行命令,並向客戶端返回一個類型錯誤。數據庫

LLEN命令執行時的類型檢查過程
LLEN命令執行時的類型檢查過程緩存

* 多態實現

  • 基於編碼的多態--一個命令能夠同時用於處理多種不一樣的編碼。
  • 基於類型的多態--一個命令能夠同時用於處理多種不一樣類型的鍵。

LLEN命令執行過程
LLEN命令執行過程服務器

* 空轉時長

lru屬性記錄了對象最後一次被命令程序訪問的時間。
OBJECT IDLETIME 命令能夠打印給定鍵的空轉時長,等於當前時間減去鍵的值對象的lru時間。
若是服務器打開了maxmemory選項,而且內存回收算法爲volatile-lru或者allkeys-lru,那麼服務器佔用的內存數超過maxmemory選項設置的上限空轉時長較高的部分鍵會優先被釋放,回收內存。數據結構

* 內存回收

對象系統構建了一個引用計數技術實現的內存回收機制。
對象的引用計數信息會隨着對象的使用狀態而不斷變化:ide

  • 建立一個新對象時,引用計數值初始化爲1;
  • 當對象被一個新程序使用時,引用計數值增1;
  • 當對象再也不被一個程序使用時,引用計數值減1;
  • 當對象的引用計數值變爲0,對象所佔的內存將會被釋放。

* 對象共享

多個鍵共享同一個值對象須要執行如下步驟:
1)將數據庫鍵的值指針指向一個現有的值對象;
2)將被共享的值對象的引用技術增一。優化

共享對象

* 字符串對象

編碼能夠是int、raw或者embstr。編碼

  • 結構

若是字符串對象保存的是整數值,int編碼實現。設計

int編碼的字符串對象

若是字符串對象保存的是字符串值,而且長度小於等於39字節,embstr編碼實現。

embstr編碼的字符串對象

若是字符串對象保存的是字符串值,而且長度大於39字節,raw編碼實現。

raw編碼的字符串對象

  • 策略

int、embstr、raw三種不一樣編碼實現字符串對象,就是根據值對象類型優化內存分配。
embstr編碼的字符串對象較raw編碼的字符串對象將內存分配次數從兩次下降爲一次。
embstr編碼的字符串對象較raw編碼的字符串對象內存釋放也從兩次減小爲一次。
embstr編碼的字符串對象數據保存在一塊連續的內存可以更好地利用緩存的優點。
可是embstr編碼的字符串對象其實是隻讀的,int編碼的字符串對象的值變動成字符串值,對象將轉換成raw編碼的字符串對象。

  • 命令

SET:保存值。
GET:取值。
APPEND:追加字符串。
STRLEN:取字符串長度。

* 列表對象

編碼能夠是ziplist或者linkedlist。

  • 結構

ziplist編碼列表對象。

ziplist編碼列表對象

linkedlist編碼列表對象。

linkedlist編碼列表對象

  • 策略

同時知足如下兩個條件時,列表對象使用ziplist編碼:
1)列表對象保存的全部字符串元素的長度都小於64字節;
2)列表對象保存的元素數量小於512個。
任意一個條件不知足,列表對象使用linkedlist編碼。
配置:list-max-ziplist-value選項和list-max-ziplist-entries選項。

  • 命令

RPUSH:添加列表表頭元素。
LPOP:彈出列表表頭元素。
LINSERT:插入元素。
LLEN:取列表長度。

* 哈希對象

編碼能夠是ziplist或者hashtable。

  • 結構

ziplist編碼哈希對象

ziplist編碼哈希對象

hashtable編碼哈希對象

hashtable編碼哈希對象

  • 策略

同時知足如下兩個條件,哈希對象使用ziplist編碼:
1)哈希對象保存的全部鍵值對對象的鍵和值的字符串長度都小於64字節;
2)哈希對象保存的鍵值對數量小於512個。
任意一個條件不知足,哈希對象使用hashtable編碼。
配置:hash-max-ziplist-value選項和hash-max-ziplist-entries選項。

  • 命令

HSET:添加元素。
HGET:獲取元素。
HDEL:刪除元素。
HLEN:取哈希元素數量

* 集合對象

編碼能夠是intset或者hashtable。

  • 結構

intset編碼集合對象。

intset編碼集合對象

hashtable編碼集合對象。

hashtable編碼集合對象

  • 策略

同時知足如下兩個條件,集合對象使用intset編碼:
1)集合對象保存的全部元素都是整數值;
2)集合對象保存的元素數量不超過512個。
任意一個條件不知足,集合對象使用hashtable編碼。

  • 命令

SADD:添加元素。
SPOP:彈出元素。
SCARD:取集合元素數量。

* 有序集合對象

編碼能夠是ziplist或者skiplist。

  • 結構

ziplist編碼有序集合對象。

ziplist編碼有序集合對象

skiplist編碼有序集合對象。

skiplist編碼有序集合對象

  • 策略

同時知足如下兩個條件,有序集合對象使用ziplist編碼:
1)有序集合對象保存的全部成員的長度都小於64字節;
2)有序集合對象保存的元素數量小於128個。
任意一個條件不知足,序集合對象使用skiplist編碼。

  • 命令

ZADD:添加元素。
ZCARD:取有序集合對象元素數量。
ZRANK:從表頭向表尾遍歷有序集合。
ZSCORE:取給定成員分值。

參考文獻:《redis設計與實現》

相關文章
相關標籤/搜索