Redis源碼解析:08對象

        前面介紹了Redis用到的全部主要數據結構,好比簡單動態字符串(SDS)、雙端鏈表、字典、壓縮列表、整數集合等。然而Redis並無直接使用這些數據結構來實現鍵值對數據庫,而是基於這些數據結構建立了一個對象系統,包括字符串對象、列表對象、哈希對象、集合對象和有序集合對象這五種類型的對象,每種對象都用到了至少一種前面所介紹的數據結構。git

 

        Redis在執行命令以前,根據對象的類型來判斷一個對象是否能夠執行給定的命令。還能夠針對不一樣的使用場景,爲對象設置多種不一樣的數據結構實現,從而優化對象在不一樣場景下的內存使用效率。github

        Redis的對象系統還實現了基於引用計數的內存回收機制,當某個對象的引用計數爲0時,這個對象所佔用的內存就會被自動釋放;Redis還經過引用計數技術實現了對象共享機制,經過讓多個鍵共享同一個對象來節約內存。redis

 

        最後,Redis的對象帶有訪間時間記錄信息,在服務器啓用了maxmemory功能的狀況下,長時間未使用的那些鍵可能會優先被服務器刪除。算法

 

一:對象的類型和編碼數據庫

        Redis使用對象來表示數據庫中的鍵和值,每當在Redis的數據庫中新建立一個鍵值對時,至少會建立兩個對象,一個對象用做鍵,另外一個對象用做值。好比下面的命令:緩存

127.0.0.1:6379> set msg "hello world"
OK

        在數據庫中建立了一個新的鍵值對,其中鍵是一個包含了字符串值"msg"的對象,而值則是一個包含了字符串值"hello  world"的對象。服務器

        對於Redis中的鍵值對來講,鍵老是一個字符申對象,而值能夠是字符串對象、列表對象、哈希對象、集合對象或者有序集合對象。數據結構

 

        Redis中的每一個對象都由一個redisObject結構表示,該結構在redis.h中定義:app

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

        type字段表示對象的類型,取值能夠是以下內容之一:函數

#define REDIS_STRING 0  
#define REDIS_LIST 1  
#define REDIS_SET 2  
#define REDIS_ZSET 3  
#define REDIS_HASH 4  

        Redis中的TYPE命令能夠返回對象的類型:

127.0.0.1:6379> type msg
string

        redisObject的ptr指針指向對象底層的數據結構,而數據結構的類型由redisObject的encoding字段決定。取值能夠是以下內容之一:

#define REDIS_ENCODING_RAW 0     /* Raw representation */  
#define REDIS_ENCODING_INT 1     /* Encoded as integer */  
#define REDIS_ENCODING_HT 2      /* Encoded as hash table */  
#define REDIS_ENCODING_ZIPMAP 3  /* Encoded as zipmap */  
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */  
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */  
#define REDIS_ENCODING_INTSET 6  /* Encoded as intset */  
#define REDIS_ENCODING_SKIPLIST 7  /* Encoded as skiplist */  
#define REDIS_ENCODING_EMBSTR 8  /* Embedded sds string encoding */  

        其中,字符串對象可使用的編碼有:REDIS_ENCODING_RAW 、REDIS_ENCODING_INT和REDIS_ENCODING_EMBSTR,列表對象可使用的編碼有:REDIS_ENCODING_LINKEDLIST和REDIS_ENCODING_ZIPLIST,哈希對象可使用的編碼有:REDIS_ENCODING_HT、和REDIS_ENCODING_ZIPLIST,集合對象可使用的編碼有:REDIS_ENCODING_HT和REDIS_ENCODING_INTSET,有序集合可使用的編碼有:REDIS_ENCODING_ZIPLIST和REDIS_ENCODING_SKIPLIST。

        在Redis的2.6版本以前,包含少許鍵值對的哈希對象使用REDIS_ENCODING_ZIPMAP編碼。自2.6開始,開始使用REDIS_ENCODING_ZIPLIST,做爲包含少許鍵值對的哈希對象的實現。

 

        使用DBJECT  ENCDDING命令能夠查看一個值的編碼:

127.0.0.1:6379> object encoding msg
"embstr"

        對象在不一樣的場景下,使用不一樣的數據結構來實現,極大地提高了Redis的靈活性和效率。好比列表對象包含的元素比較少時,Redis使用壓縮列表做爲列表對象的底層實現,這樣作更加節約內存,而且在內存中以連續塊方式保存的壓縮列表比起雙端鏈表,能夠更快被載人到緩存中。隨着列表對象包含的元素愈來愈多,使用壓縮列表來保存元素的優點逐漸消失時,對象就會將底層實現從壓縮列表轉向雙端鏈表上面。

 

二:字符串對象

         字符串對象的編碼能夠是int、raw或者embstr。

        若是一個字符串對象保存的是整數值,而且這個整數值能夠用long類型來表示,那字符串對象會將整數值保存在字符串對象redisObject結構的ptr屬性裏面,也就是將void*轉換成long。內存佈局以下:

        好比:

127.0.0.1:6379> set number 123
OK
127.0.0.1:6379> object encoding number
"int"

        若是字符串對象保存的是一個字符串值,而且這個字符串值的長度大於39字節,那麼字符串對象使用一個簡單動態字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲raw。內存佈局以下,注意,對象的ptr指針,不是指向sdshdr的首地址,而是指向原始字符串的首地址:

 

        若是字符串對象保存的是一個字符串值,而且這個字符串值的長度小於等於39字節,那麼字符串對象將使用embstr編碼的方式來保存這個字符串值。

        embstr編碼是專門用於保存短字符串的一種優化編碼方式,這種編碼和raw編碼同樣,都使用redisObject結構和sdshdr結構來表示字符串對象。

        但raw編碼會調用兩次內存分配函數來分別建立redisObject結構和sdshdr結構,而embstr編碼則經過調用一次內存分配函數來分配一塊連續的空間,空間中依次包含redisObject和sdshdr結構。這樣作的好處是:內存的分配和釋放更加高效,並且embstr編碼的字符串對象,使用連續的內存塊,可以更好地利用緩存帶來的優點。其內存佈局以下:

         好比:

127.0.0.1:6379> set msg "abcdefghijklmnopqrstuvwxyz0123456789012"
OK
127.0.0.1:6379> object encoding msg
"embstr"
127.0.0.1:6379> set msg "abcdefghijklmnopqrstuvwxyz01234567890123"
OK
127.0.0.1:6379> object encoding msg
"raw"

        int編碼的字符串對象和embstr編碼的字符申對象,在知足必定條件的狀況下,會被轉換爲raw編碼的字符串對象。

        int編碼的字符串對象,若是向對象執行了一些命令,使得這個對象保存的再也不是整數值,而是一個字符串值,那麼字符串對象的編碼將從int變爲raw。好比下面的例子:

127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> object encoding number
"int"
127.0.0.1:6379> append number " is a number"
(integer) 13
127.0.0.1:6379> object encoding number
"raw"

        Redis沒有爲embstr編碼的字符串對象編寫任何相應的修改程序(只有int編碼的字符串對象和raw編碼的字符串對象有這些程序),因此embstr編碼的字符串對象其實是隻讀的。

        當對embstr編碼的字符串對象執行任何修改命令時,程序會先將對象的編碼從embstr轉換成raw,而後再執行修改命令。由於這個緣由,embstr編碼的字符串對象在執行修改命令以後,總會變成一個raw編碼的字符串對象。好比:

127.0.0.1:6379> set msg "hello"
OK
127.0.0.1:6379> object encoding msg
"embstr"
127.0.0.1:6379> append msg " world"
(integer) 11
127.0.0.1:6379> object encoding msg
"raw"

三:列表對象

        列表對象的編碼能夠是ziplist或者linkedlist。

        當列表對象中的元素較少,且元素都是較短的字符串時,列表對象採用ziplist編碼,也就是使用壓縮列表(ziplist)做爲底層實現,每一個壓縮列表節點(entry)保存一個列表元素。

        好比:

127.0.0.1:6379> rpush number 1 "three" 5
(integer) 3
127.0.0.1:6379> object encoding number
"ziplist"

        採用ziplist編碼時,內存佈局以下,在ziplist中,每一個節點中保存的是原始字符串(「three」)或者數字(123),而非redisObject結構,或者sdshdr結構:

 

        當列表對象能夠同時知足如下兩個條件時,纔會使用ziplist編碼:

        a:列表對象中,每一個元素保存的字符串元素的長度都小於64字節;

        b:列表對象保存的元素數量小於512個。

        當條件不知足時,列表對象使用linkedlist編碼,也就是採用雙端鏈表(list)做爲底層實現,每一個鏈表節點(listNode)保存一個列表元素(字符串對象)。

 

        列表對象採用linkedlist編碼時,內存佈局以下,在list鏈表中,每一個節點listNode結構中的value指針,指向一個redisObject結構:

        linkedlist編碼的列表對象,在底層的雙端鏈表結構中包含了多個字符串對象,這種嵌套字符串對象的行爲在稍後介紹的哈希對象、集合對象和有序集合對象中都會出現。字符串對象是Redis五種類型的對象中,惟一一種會被其餘四種類型對象嵌套的對象。

 

        對於使用ziplist編碼的列表對象,當插入操做使得兩個條件中的任意一個不知足時,就會執行編碼轉換。本來保存在壓縮列表裏的全部列表元素都會被轉移並保存到雙端鏈表中,對象的編碼從ziplist變爲linkedlist。

        好比,下面分別演示了插入長度超過64字節的元素,以及元素數超過512的狀況:

127.0.0.1:6379> rpush number "0123456789012345678901234567890123456789012345678901234567890123"
(integer) 4
127.0.0.1:6379> object encoding number
"ziplist"
127.0.0.1:6379> rpush number "01234567890123456789012345678901234567890123456789012345678901234"
(integer) 5
127.0.0.1:6379> object encoding number
"linkedlist"

127.0.0.1:6379> EVAL "for i=1, 512 do redis.call('RPUSH', KEYS[1], i) end" 1 "integers"
(nil)
127.0.0.1:6379> llen integers
(integer) 512
127.0.0.1:6379> object encoding integers
"ziplist"
127.0.0.1:6379> rpush integers 513
(integer) 513
127.0.0.1:6379> object encoding integers
"linkedlist"

        這兩個條件中的閾值,能夠經過配置文件中的如下選項進行修改:

list-max-ziplist-entries  512  
list-max-ziplist-value  64

四:哈希對象

        哈希對象的編碼能夠是:ziplist或者hashtable。

        當哈希對象中的鍵值對較少,且鍵值對中保存的都是較短的字符串時,哈希對象採用zipiist編碼,也就是使用壓縮列表(ziplist)做爲底層實現。當有新的鍵值對要加人到哈希對象時,會分別將鍵和值中的原始字符串添加到壓縮列表的表尾。

        所以,壓縮列表中,保存了同一鍵值對的兩個節點老是緊挨在一塊兒,保存鍵的節點在前,保存值的節點在後。先添加到哈希對象中的鍵值對在前,後添加的鍵值對在後。

        好比:

127.0.0.1:6379> hset profile name "tom"
(integer) 1
127.0.0.1:6379> hset profile age 25
(integer) 1
127.0.0.1:6379> hset profile career "programmer"
(integer) 1
127.0.0.1:6379> object encoding profile
"ziplist" 

        哈希對象採用ziplist編碼時,內存佈局與ziplist編碼的列表對象相似。在壓縮列表中,相鄰兩個節點中保存的,分別是key和value的原始字符串,而非redisObject結構,或者sdshdr結構,好比下圖,就表示上面例子中,哈希對象profile的內存結構:

 

        當哈希對象能夠同時知足如下兩個條件時,纔會使用ziplist編碼:

        a:哈希對象中,全部鍵值對中,保存的字符串元素的長度都小於等於64字節;

        b:列表對象保存的鍵值對數量小於等於512個。

 

        當條件之一不知足時,哈希對象使用hashtable編碼,也就是採用字典(dict)做爲底層實現,字典的哈希表中,每一個哈希節點(dictEntry)保存一個鍵值對(兩個字符串對象)。        

        哈希對象採用hashtable編碼時,內存佈局以下,在dict字典的哈希表dictht中,每一個哈希表節點dictEntry結構中,key指針指向一個redisObject結構,表示鍵對象,v.val指針指向一個redisObject結構,表示值對象:

        對於使用ziplist編碼的哈希對象來講,當插入操做使得兩個條件中的任意一個不能被知足時,就會執行編碼轉換。本來保存在壓縮列表裏的全部鍵值對都會被轉移並保存到字典中,對象的編碼從ziplist變爲hashtable。

        好比,下面分別演示了插入長度超過64字節的鍵、值,以及鍵值對數目超過512的狀況:

127.0.0.1:6379> hset testhash 0123012345678901234567890123456789012345678901234567890123456789 "hehe2"
(integer) 1
127.0.0.1:6379> object encoding testhash
"ziplist"
127.0.0.1:6379> hset testhash 01234012345678901234567890123456789012345678901234567890123456789 "hehe2"
(integer) 1
127.0.0.1:6379> object encoding testhash
"hashtable"

127.0.0.1:6379> hset testhash2 name "0123012345678901234567890123456789012345678901234567890123456789"
(integer) 1
127.0.0.1:6379> object encoding testhash2
"ziplist"
127.0.0.1:6379> hset testhash2 name2 "01234012345678901234567890123456789012345678901234567890123456789"
(integer) 1
127.0.0.1:6379> object encoding testhash2
"hashtable"

127.0.0.1:6379> EVAL "for i=1, 512 do redis.call('HSET', KEYS[1], i, i) end" 1 "testhash3"
(nil)
127.0.0.1:6379> hlen testhash3
(integer) 512
127.0.0.1:6379> object encoding testhash3
"ziplist"
127.0.0.1:6379> hset testhash3 name "tom"
(integer) 1
127.0.0.1:6379> hlen testhash3
(integer) 513
127.0.0.1:6379> object encoding testhash3
"hashtable"

        這兩個條件中的閾值,能夠經過配置文件中的如下選項進行修改:

hash-max-ziplist-entries  512  
hash-max-ziplist-value  64  

五:集合對象

        集合對象的編碼能夠是intset或者hashtable。

        當集合對象中的元素較少,且都是整數時,集合對象採用intset編碼,也就是使用整數集合(intset)做爲底層實現。好比:

127.0.0.1:6379> sadd numbers 1 2 3
(integer) 3
127.0.0.1:6379> object encoding numbers
"intset"

        集合對象採用intset編碼時,內存佈局以下,redisObject中的ptr指向一個整數集合,在整數集合的contents中直接保存整數,而非redisObject結構,或者sdshdr結構:

 

        當集合對象能夠同時知足如下兩個條件時,纔會使用intset編碼:

        a:集合對象保存的全部元素都是整數值;

        b:集合對象保存的元素數量不超過512個。

        當條件之一不知足時,集合對象使用hashtable編碼,也就是採用字典(dict)做爲底層實現,字典的哈希表中,每一個哈希節點(dictEntry)的key保存一個元素(字符串對象),並將哈希節點的value置爲NULL。

        集合對象採用hashtable編碼時,內存佈局以下,在dict字典的哈希表dictht中,每一個哈希表節點dictEntry結構中,key指針指向一個redisObject結構,表示集合元素,v.val直接置爲NULL:

        對於使用intset編碼的集合對象來講,當插入操做使得兩個條件中的任意一個不能被知足時,就會執行編碼轉換。本來保存在整數集合裏的全部元素都會被轉移並保存到字典中,對象的編碼從intset變爲hashtable。

        好比,下面分別演示了插入非整數的元素,以及元素數目超過512的狀況:

127.0.0.1:6379> sadd numbers 1 2 3
(integer) 3
127.0.0.1:6379> object encoding numbers
"intset"
127.0.0.1:6379> sadd numbers hehe
(integer) 1
127.0.0.1:6379> object encoding numbers
"hashtable"

127.0.0.1:6379> EVAL "for i=1, 512 do redis.call('SADD', KEYS[1], i) end" 1 integers
(nil)
127.0.0.1:6379> scard integers
(integer) 512
127.0.0.1:6379> object encoding integers
"intset"
127.0.0.1:6379> sadd integers 1000
(integer) 1
127.0.0.1:6379> scard integers
(integer) 513
127.0.0.1:6379> object encoding integers
"hashtable"

        條件中的閾值,能夠經過配置文件中的如下選項進行修改:

set-max-intset-entries  512  

五:有序集合對象

        有序集合的編碼能夠是ziplist或者skiplist。

        當集合對象中的元素較少,且元素中的字符串長度較小時,有序集合對象採用ziplist編碼,也就是使用壓縮列表(ziplist)做爲底層實現。

        每一個集合元素使用兩個緊挨的壓縮列表節點來保存,第一個節點保存元素的成員(member),而第二個元素則保存元素的分值(score)。壓縮列表內的集合元素按分值從小到大進行排序,分值較小的元素被放置在靠近表頭的方向,而分值較大的元素則被放置在靠近表尾的方向。

        好比:

127.0.0.1:6379> zadd price 8.5 apple 5.0 banana 6.0 cherry
(integer) 3
127.0.0.1:6379> object encoding price
"ziplist"

        有序集合對象採用ziplist編碼時,內存佈局與ziplist編碼的列表對象相似。在壓縮列表中,相鄰兩個節點中保存的,分別是元素成員和分值的原始字符串和double浮點數,而非redisObject結構,或者sdshdr結構,好比下圖,就表示上例中,有序集合price的內存結構:

 

        當有序集合對象能夠同時知足如下兩個條件時,纔會使用ziplist編碼:

        a:有序集合保存的元素數量小於等於128個;

        b:有序集合保存的全部元素中,原始字符串的長度都小於等於64字節。

 

        當條件之一不知足時,有序集合對象使用skiplist編碼,也就是採用zset結構做爲底層實現,zset由跳躍表(skiplist)和字典(dict)兩種數據結構組成:

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

        zset結構中的跳躍表zskiplist,按分值從小到大保存全部集合元素,每一個跳躍表節點zskiplistNode中,obj屬性保存了元素的成員,而score屬性則保存了元素的分值。經過這個跳躍表,程序能夠對有序集合進行範圍型操做,好比ZRANK,ZRANGE等命令就是基於跳躍表API來實現的。

        除此以外,zset結構中的dict字典,爲有序集合建立了一個從成員到分值的映射,字典中的每一個鍵值對都保存了一個集合元素:字典的鍵保存了元素的成員,而字典的值則保存了元素的分值。經過這個字典,程序能夠用O(1)複雜度查找給定成員的分值,ZSCORE命令就是根據這一特性實現的。

        有序集合每一個元素的成員都是一個字符串對象,而每一個元素的分值都是一個double類型的浮點數。雖然zset結構同時使用跳躍表和字典來保存有序集合元素,但這兩種數據結構會經過指針來共享相同元素的成員和分值,因此同時使用跳躍表和字典來保存集合元素不會產生任何重複成員或者分值,也不會所以而浪費額外的內存。

        有序集合對象採用skiplist編碼時,內存佈局以下,在skiplist的節點zskiplistNode中,obj指針指向一個redisObject結構,表示有序集合元素的成員,在dict字典的哈希表dictht中,每一個哈希表節點dictEntry結構中,key指針指向一個同一個redisObject結構;zskiplistNode中的score記錄有序集合元素的分數,在dict字典的哈希表dictht中,每一個哈希表節點dictEntry結構中,在v.val指向該zskiplistNode中的score:

        對於使用ziplist編碼的有序集合對象來講,當插入操做使得兩個條件中的任意一個不能被知足時,就會執行編碼轉換。本來保存在壓縮列表裏的全部元素都會被轉移並保存到zset結構中,對象的編碼從ziplist變爲skiplist。

        好比,下面分別演示了插入長度超過64字節的元素,以及鍵值對數目超過128的狀況:

127.0.0.1:6379> zadd testzset 1.0 0123012345678901234567890123456789012345678901234567890123456789
(integer) 1
127.0.0.1:6379> object encoding testzset
"ziplist"
127.0.0.1:6379> zadd testzset 2.0 01234012345678901234567890123456789012345678901234567890123456789
(integer) 1
127.0.0.1:6379> object encoding testzset
"skiplist"

127.0.0.1:6379> EVAL "for i=1, 128 do redis.call('ZADD', KEYS[1], i, i) end" 1 testzset2
(nil)
127.0.0.1:6379> zcard testzset2
(integer) 128
127.0.0.1:6379> object encoding testzset2
"ziplist"
127.0.0.1:6379> zadd testzset2 3.14 pi
(integer) 1
127.0.0.1:6379> zcard testzset2
(integer) 129
127.0.0.1:6379> object encoding testzset2 
"skiplist"

        以上兩個條件中的閾值,能夠經過配置文件中的如下選項進行修改:

zset-max-ziplist-entries  128  
zset-max-ziplist-value  64  

 

六:命令的執行

        Redis中用於操做鍵的命令基本上能夠分爲兩種類型:

        一種命令能夠對任何類型的鍵執行,好比DEL、EXPIRE、TYPE、OBJECT命令等;

        另外一種命令只能對特定類型的鍵執行,好比:SET,GET,APPEND,STRLEN等命令只能對字符串鍵執行。

        使用一種數據類型的特定命令操做另外一種數據類型的健會提示錯誤:」(error) WRONG TYPE Operation against a key holding the wrong kind ofvalue」。

        所以,在執行一個類型特定的命令以前,Redis會先檢查輸人鍵的類型是否正確,而後再決定是否執行給定的命令。類型檢查是經過redisObject結構的type屬性來實現的。

 

        Redis除了會根據值對象的類型來判斷鍵是否可以執行指定命令以外,還會根據值對象的編碼方式,選擇正確的函數來執行命令。

        好比,列表對象的編碼有ziplist和linkedlist兩種,若是對一個列表鍵執行LLEN命令,那麼服務器須要根據鍵的值對象所使用的編碼來選擇正確的LLEN命令實現,若是編碼爲ziplist,將調用ziplistLen函數來返回壓縮列表的長度;若是編碼爲linkedlist,將調用listLength函數來返回雙端鏈表的長度。

 

七:引用計數

         利用redisObject結構的引用計數,也就是refcount屬性,能夠實現內存回收,以及對象共享機制。

 

        C語言不具有內存自動回收功能,因此Redis在本身的對象系統中構建了一個引用計數技術,實現內存回收機制。經過這一機制,程序能夠經過跟蹤對象的引用計數信息,在適當的時候自動釋放對象並進行內存回收。

        對象的引用計數會隨着對象的使用狀態而不斷變化:

建立一個新對象時,引用計數的值會被初始化爲1;

當對象被一個新程序使用時,它的引用計數值會加1;

當對象再也不被一個程序使用時,它的引用計數值會減1;

當對象的引用計數值變爲0時,對象所佔用的內存會被釋放。

 

        除了用於實現內存回收機制以外,對象的引用計數屬性還帶有對象共享的做用。好比,假設鍵A建立了一個整數值100的字符串對象做爲值對象,若是鍵B也要建立一個整數值100的字符串對象做爲值對象,那麼Redis會讓鍵A和鍵B共享同一個字符串對象。這樣作能夠節約內存。

        在 Redis中,讓多個鍵共享同一個值對象,只須要將共享的值對象的引用計數加1便可。像前面介紹有序集合對象的skiplist編碼時,dict和skiplist兩種數據結構就共享同一個redisObject對象做爲元素的成員。

 

        默認狀況下,Redis會在初始化服務器時,會建立10000個字符串對象,這些對象包含了從0到9999的全部整數值,當須要用到值爲0到9 999的字符串對象時,服務器就會使用這些共享對象.而不是新建立對象。

        好比:若是建立一個值爲100的鍵a,並使用OBJECT  REFCOUNT命令查看鍵a的值對象的引用計數,會發現值對象的引用計數爲2,這就是由於鍵a的值,共享了Redis預先建立好的對象的緣故:

127.0.0.1:6379> set a 100
OK
127.0.0.1:6379> object refcount a
(integer) 2


八:對象的空轉時間

        除了前面介紹過的type,encoding,ptr和refcount四個屬性以外,redisObject結構包含的最後一個屬性爲lru屬性,該屬性記錄了對象最後一次被程序訪問的時間。

        OBJECT  IDLETIME命令能夠打印出給定鍵的空轉時長,這一空轉時長就是經過將當前時間減去鍵的值對象的lru時間計算得出的:

127.0.0.1:6379> set msg "hello"
OK
127.0.0.1:6379> object idletime msg
(integer) 6
127.0.0.1:6379> object idletime msg
(integer) 23
127.0.0.1:6379> get msg
"hello"
127.0.0.1:6379> object idletime msg
(integer) 1

        鍵的空轉時長還有另一個做用,若是服務器打開了maxmemoxy選項,而且服務器用於回收內存的算法爲volatile-lru或者allkeys-lru,那麼當服務器佔用的內存數超過了maxmemory選項所設置的閾值時,空轉時長較高的那部分鍵會優先被服務器釋放,從而回收內存。

 

        對象相關的代碼,能夠參閱:

https://github.com/gqtc/redis-3.0.5/blob/master/redis-3.0.5/src/object.c

相關文章
相關標籤/搜索