redis中的對象

前言

在上一篇關(guān)于redis的文章中们豌,我們分析了redis用到的主要的數(shù)據(jù)結(jié)構(gòu),但是redis并沒有直接使用這些數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)KV形式的數(shù)據(jù)庫,而是基于這些數(shù)據(jù)結(jié)構(gòu)又封裝了一些對象崖疤。常用的基礎(chǔ)對象包括字符串嗜闻、列表蜕依、哈希意荤、集合和有序集合5種,而每種對象底層的實現(xiàn)就是上一篇提到的數(shù)據(jù)結(jié)構(gòu)中的一種或多種辆沦。參考《redis設(shè)計與實現(xiàn)》权烧,redis這么設(shè)計有以下幾點的好處:

  1. redis執(zhí)行命令前可以根據(jù)對象的具體類型來判斷是否可以執(zhí)行,比如hset作用于字符串對象就會出錯檐束;
  2. 同一個對象底層可以有多種數(shù)據(jù)結(jié)構(gòu)實現(xiàn)辫秧,可以針對不同的場景做動態(tài)替換,優(yōu)化效率厢塘;
  3. redis的對象系統(tǒng)實現(xiàn)了引用計數(shù)方式的內(nèi)存回收機制茶没,對象不再使用時占用的內(nèi)存會自動釋放;
  4. redis的對象系統(tǒng)實現(xiàn)了基于引用計數(shù)的對象共享機制晚碾,某些情況下抓半,多個數(shù)據(jù)庫鍵可以共享同一個對象;
  5. redis對象帶有訪問記錄信息格嘁,可以計算空轉(zhuǎn)時長笛求,方便內(nèi)存淘汰。

對象類型和編碼

redis中的每個對象都由redisObject結(jié)構(gòu)表示:

typedef struct redisObject {

    // 類型
    unsigned type:4;

    // 編碼
    unsigned encoding:4;

    // 對象最后一次被訪問的時間
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

    // 引用計數(shù)
    int refcount;

    // 指向?qū)嶋H值的指針
    void *ptr;

} robj;

type代表對象的類型糕簿,取值可以是以下幾種探入,redis支持使用TYPE命令來查看數(shù)據(jù)庫鍵對應(yīng)的值對象的類型

對象 對象 type 屬性的值 TYPE 命令的輸出
字符串對象 REDIS_STRING "string"
列表對象 REDIS_LIST "list"
哈希對象 REDIS_HASH "hash"
集合對象 REDIS_SET "set"
有序集合對象 REDIS_ZSET "zset"

encoding屬性記錄了對象使用的編碼,其實就是底層用了哪個數(shù)據(jù)結(jié)構(gòu)懂诗,可以使用OBJECT ENCODING 命令來查看數(shù)據(jù)庫鍵的值對象的編碼蜂嗽。

類型 編碼 對象 OBJECT ENCODING 命令輸出
REDIS_STRING REDIS_ENCODING_INT 使用整數(shù)值實現(xiàn)的字符串對象。 "int"
REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 編碼的簡單動態(tài)字符串實現(xiàn)的字符串對象殃恒。 "embstr"
REDIS_STRING REDIS_ENCODING_RAW 使用簡單動態(tài)字符串實現(xiàn)的字符串對象植旧。 "raw"
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的列表對象。 "ziplist"
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用雙端鏈表實現(xiàn)的列表對象离唐。 "linkedlist"
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的哈希對象病附。 "ziplist"
REDIS_HASH REDIS_ENCODING_HT 使用字典實現(xiàn)的哈希對象。 "hashtable"
REDIS_SET REDIS_ENCODING_INTSET 使用整數(shù)集合實現(xiàn)的集合對象亥鬓。 "intset"
REDIS_SET REDIS_ENCODING_HT 使用字典實現(xiàn)的集合對象完沪。 "hashtable"
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的有序集合對象。 "ziplist"
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳躍表和字典實現(xiàn)的有序集合對象嵌戈。 "skiplist"

字符串對象

從上一個表格中可以看到覆积,redis中字符串的表示可以是int、embstr后者raw熟呛。具體以哪種方式存儲是需要根據(jù)不同的條件來決定的技健。

編碼
可以用 long 類型保存的整數(shù)。 int
可以用 long double 類型保存的浮點數(shù)惰拱。 embstr 或者 raw
字符串值雌贱, 或者因為長度太大而沒辦法用 long 類型表示的整數(shù)啊送, 又或者因為長度太大而沒辦法用 long double 類型表示的浮點數(shù)。 embstr 或者 raw
  1. long類型的整數(shù)用int類型
  2. 字符串小于等于32字節(jié)用embstr類型
  3. 字符串大于32字節(jié)用raw類型
  4. Long轉(zhuǎn)為字符串的時候會從int轉(zhuǎn)為raw類型(如對int類型做APPEND操作)
  5. embstr類型是只讀的欣孤,如果對字符串變更馋没,會轉(zhuǎn)為raw類型(如APPEND操作)

附一個《redis設(shè)計與實現(xiàn)》中的字符串命令實現(xiàn)的表格:

命令 int 編碼的實現(xiàn)方法 embstr 編碼的實現(xiàn)方法 raw 編碼的實現(xiàn)方法
SET 使用 int 編碼保存值。 使用 embstr 編碼保存值降传。 使用 raw 編碼保存值篷朵。
GET 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值婆排, 然后向客戶端返回這個字符串值声旺。 直接向客戶端返回字符串值。 直接向客戶端返回字符串值段只。
APPEND 將對象轉(zhuǎn)換成 raw 編碼腮猖, 然后按 raw 編碼的方式執(zhí)行此操作。 將對象轉(zhuǎn)換成 raw 編碼赞枕, 然后按 raw 編碼的方式執(zhí)行此操作澈缺。 調(diào)用 sdscatlen 函數(shù), 將給定字符串追加到現(xiàn)有字符串的末尾炕婶。
INCRBYFLOAT 取出整數(shù)值并將其轉(zhuǎn)換成 longdouble 類型的浮點數(shù)姐赡, 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來柠掂。 取出字符串值并嘗試將其轉(zhuǎn)換成long double 類型的浮點數(shù)项滑, 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來涯贞。 如果字符串值不能被轉(zhuǎn)換成浮點數(shù)枪狂, 那么向客戶端返回一個錯誤。 取出字符串值并嘗試將其轉(zhuǎn)換成 longdouble 類型的浮點數(shù)肩狂, 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來姥饰。 如果字符串值不能被轉(zhuǎn)換成浮點數(shù)傻谁, 那么向客戶端返回一個錯誤。
INCRBY 對整數(shù)值進行加法計算列粪, 得出的計算結(jié)果會作為整數(shù)被保存起來审磁。 embstr 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤岂座。 raw 編碼不能執(zhí)行此命令态蒂, 向客戶端返回一個錯誤。
DECRBY 對整數(shù)值進行減法計算费什, 得出的計算結(jié)果會作為整數(shù)被保存起來钾恢。 embstr 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤。 raw 編碼不能執(zhí)行此命令瘩蚪, 向客戶端返回一個錯誤泉懦。
STRLEN 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值疹瘦, 計算并返回這個字符串值的長度崩哩。 調(diào)用 sdslen 函數(shù), 返回字符串的長度言沐。 調(diào)用 sdslen 函數(shù)邓嘹, 返回字符串的長度。
SETRANGE 將對象轉(zhuǎn)換成 raw 編碼险胰, 然后按 raw 編碼的方式執(zhí)行此命令汹押。 將對象轉(zhuǎn)換成 raw 編碼, 然后按 raw 編碼的方式執(zhí)行此命令鸯乃。 將字符串特定索引上的值設(shè)置為給定的字符鲸阻。
GETRANGE 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值缨睡, 然后取出并返回字符串指定索引上的字符鸟悴。 直接取出并返回字符串指定索引上的字符。 直接取出并返回字符串指定索引上的字符奖年。

列表對象

列表對象的編碼可以是壓縮列表或者雙端鏈表细诸,也就是ziplist或者linkedlist。壓縮列表的存儲結(jié)構(gòu)可以參見上一篇介紹redis數(shù)據(jù)結(jié)構(gòu)的文章陋守,而雙端鏈表底層每個節(jié)點都保存了一個字符串對象震贵,字符串對象也是唯一一種會被其他對象嵌套引用的對象。

當(dāng)列表對象可以同時滿足以下兩個條件時水评,列表對象使用 ziplist 編碼猩系,具體的長度和數(shù)量的限制值也可以通過配置文件進行配置:

  • 列表對象保存的所有字符串元素的長度都小于 64 字節(jié);
  • 列表對象保存的元素數(shù)量小于 512 個中燥;
    同樣附上列表命令的實現(xiàn)表格:
命令 ziplist 編碼的實現(xiàn)方法 linkedlist 編碼的實現(xiàn)方法
LPUSH 調(diào)用 ziplistPush 函數(shù)寇甸, 將新元素推入到壓縮列表的表頭。 調(diào)用 listAddNodeHead 函數(shù)疗涉, 將新元素推入到雙端鏈表的表頭拿霉。
RPUSH 調(diào)用 ziplistPush 函數(shù), 將新元素推入到壓縮列表的表尾咱扣。 調(diào)用 listAddNodeTail 函數(shù)绽淘, 將新元素推入到雙端鏈表的表尾。
LPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表頭節(jié)點闹伪, 在向用戶返回節(jié)點所保存的元素之后沪铭, 調(diào)用 ziplistDelete 函數(shù)刪除表頭節(jié)點壮池。 調(diào)用 listFirst 函數(shù)定位雙端鏈表的表頭節(jié)點, 在向用戶返回節(jié)點所保存的元素之后伦意, 調(diào)用 listDelNode 函數(shù)刪除表頭節(jié)點火窒。
RPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表尾節(jié)點, 在向用戶返回節(jié)點所保存的元素之后驮肉, 調(diào)用 ziplistDelete 函數(shù)刪除表尾節(jié)點熏矿。 調(diào)用 listLast 函數(shù)定位雙端鏈表的表尾節(jié)點, 在向用戶返回節(jié)點所保存的元素之后离钝, 調(diào)用 listDelNode 函數(shù)刪除表尾節(jié)點票编。
LINDEX 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表中的指定節(jié)點, 然后返回節(jié)點所保存的元素卵渴。 調(diào)用 listIndex 函數(shù)定位雙端鏈表中的指定節(jié)點慧域, 然后返回節(jié)點所保存的元素。
LLEN 調(diào)用 ziplistLen 函數(shù)返回壓縮列表的長度浪读。 調(diào)用 listLength 函數(shù)返回雙端鏈表的長度昔榴。
LINSERT 插入新節(jié)點到壓縮列表的表頭或者表尾時, 使用 ziplistPush 函數(shù)碘橘; 插入新節(jié)點到壓縮列表的其他位置時互订, 使用 ziplistInsert 函數(shù)。 調(diào)用 listInsertNode 函數(shù)痘拆, 將新節(jié)點插入到雙端鏈表的指定位置仰禽。
LREM 遍歷壓縮列表節(jié)點, 并調(diào)用 ziplistDelete 函數(shù)刪除包含了給定元素的節(jié)點纺蛆。 遍歷雙端鏈表節(jié)點吐葵, 并調(diào)用 listDelNode 函數(shù)刪除包含了給定元素的節(jié)點。
LTRIM 調(diào)用 ziplistDeleteRange 函數(shù)桥氏, 刪除壓縮列表中所有不在指定索引范圍內(nèi)的節(jié)點温峭。 遍歷雙端鏈表節(jié)點, 并調(diào)用 listDelNode 函數(shù)刪除鏈表中所有不在指定索引范圍內(nèi)的節(jié)點字支。
LSET 調(diào)用 ziplistDelete 函數(shù)凤藏, 先刪除壓縮列表指定索引上的現(xiàn)有節(jié)點, 然后調(diào)用 ziplistInsert 函數(shù)祥款, 將一個包含給定元素的新節(jié)點插入到相同索引上面清笨。 調(diào)用 listIndex 函數(shù)月杉, 定位到雙端鏈表指定索引上的節(jié)點刃跛, 然后通過賦值操作更新節(jié)點的值。

哈希對象

哈希對象的編碼可以是ziplist或者hashtable苛萎。

  • 對于使用壓縮列表來實現(xiàn)的哈希對象桨昙,每次增加新的鍵值對會先將代表鍵的壓縮列表節(jié)點添加到表尾检号,然后再將代表值的壓縮列表節(jié)點添加到表尾,所以鍵值總是連在一起蛙酪。
  • 對于使用字典實現(xiàn)的哈希對象齐苛,不管鍵和值都是一個字符串對象。

當(dāng)哈希對象可以同時滿足以下兩個條件時桂塞, 哈希對象使用 ziplist 編碼凹蜂,同樣,兩個限制值是可以通過配置文件進行修改的:

  • 哈希對象保存的所有鍵值對的鍵和值的字符串長度都小于 64 字節(jié)阁危;
  • 哈希對象保存的鍵值對數(shù)量小于 512 個玛痊;

附上哈希命令的實現(xiàn)表格:

命令 ziplist 編碼實現(xiàn)方法 hashtable 編碼的實現(xiàn)方法
HSET 首先調(diào)用 ziplistPush 函數(shù), 將鍵推入到壓縮列表的表尾狂打, 然后再次調(diào)用 ziplistPush 函數(shù)擂煞, 將值推入到壓縮列表的表尾。 調(diào)用 dictAdd 函數(shù)趴乡, 將新節(jié)點添加到字典里面对省。
HGET 首先調(diào)用 ziplistFind 函數(shù), 在壓縮列表中查找指定鍵所對應(yīng)的節(jié)點晾捏, 然后調(diào)用 ziplistNext 函數(shù)蒿涎, 將指針移動到鍵節(jié)點旁邊的值節(jié)點, 最后返回值節(jié)點粟瞬。 調(diào)用 dictFind 函數(shù)同仆, 在字典中查找給定鍵, 然后調(diào)用 dictGetVal 函數(shù)裙品, 返回該鍵所對應(yīng)的值俗批。
HEXISTS 調(diào)用 ziplistFind 函數(shù), 在壓縮列表中查找指定鍵所對應(yīng)的節(jié)點市怎, 如果找到的話說明鍵值對存在岁忘, 沒找到的話就說明鍵值對不存在。 調(diào)用 dictFind 函數(shù)区匠, 在字典中查找給定鍵干像, 如果找到的話說明鍵值對存在, 沒找到的話就說明鍵值對不存在驰弄。
HDEL 調(diào)用 ziplistFind 函數(shù)麻汰, 在壓縮列表中查找指定鍵所對應(yīng)的節(jié)點, 然后將相應(yīng)的鍵節(jié)點戚篙、 以及鍵節(jié)點旁邊的值節(jié)點都刪除掉五鲫。 調(diào)用 dictDelete 函數(shù), 將指定鍵所對應(yīng)的鍵值對從字典中刪除掉岔擂。
HLEN 調(diào)用 ziplistLen 函數(shù)位喂, 取得壓縮列表包含節(jié)點的總數(shù)量浪耘, 將這個數(shù)量除以 2 , 得出的結(jié)果就是壓縮列表保存的鍵值對的數(shù)量塑崖。 調(diào)用 dictSize 函數(shù)七冲, 返回字典包含的鍵值對數(shù)量, 這個數(shù)量就是哈希對象包含的鍵值對數(shù)量规婆。
HGETALL 遍歷整個壓縮列表澜躺, 用 ziplistGet 函數(shù)返回所有鍵和值(都是節(jié)點)。 遍歷整個字典抒蚜, 用 dictGetKey 函數(shù)返回字典的鍵苗踪, 用 dictGetVal 函數(shù)返回字典的值。

集合對象

集合對象的編碼可以是intset或者hashtable削锰,也就是整數(shù)集合或者字典通铲。intset實現(xiàn)的集合所有元素都是整數(shù),而hashtable實現(xiàn)的集合元素則每個鍵都是字符串對象器贩,每個值都是NULL颅夺,類似于Java中HashSet的實現(xiàn)。

當(dāng)集合對象可以同時滿足以下兩個條件時蛹稍,對象使用 intset 編碼吧黄,數(shù)量上限同樣可配置:

  • 集合對象保存的所有元素都是整數(shù)值;
  • 集合對象保存的元素數(shù)量不超過 512 個唆姐;

集合命令的實現(xiàn)表格:

命令 intset 編碼的實現(xiàn)方法 hashtable 編碼的實現(xiàn)方法
SADD 調(diào)用 intsetAdd 函數(shù)拗慨, 將所有新元素添加到整數(shù)集合里面。 調(diào)用 dictAdd 奉芦, 以新元素為鍵赵抢, NULL 為值, 將鍵值對添加到字典里面声功。
SCARD 調(diào)用 intsetLen 函數(shù)烦却, 返回整數(shù)集合所包含的元素數(shù)量, 這個數(shù)量就是集合對象所包含的元素數(shù)量先巴。 調(diào)用 dictSize 函數(shù)其爵, 返回字典所包含的鍵值對數(shù)量, 這個數(shù)量就是集合對象所包含的元素數(shù)量伸蚯。
SISMEMBER 調(diào)用 intsetFind 函數(shù)摩渺, 在整數(shù)集合中查找給定的元素, 如果找到了說明元素存在于集合剂邮, 沒找到則說明元素不存在于集合摇幻。 調(diào)用 dictFind 函數(shù), 在字典的鍵中查找給定的元素, 如果找到了說明元素存在于集合囚企, 沒找到則說明元素不存在于集合。
SMEMBERS 遍歷整個整數(shù)集合瑞眼, 使用 intsetGet 函數(shù)返回集合元素龙宏。 遍歷整個字典, 使用 dictGetKey 函數(shù)返回字典的鍵作為集合元素伤疙。
SRANDMEMBER 調(diào)用 intsetRandom 函數(shù)银酗, 從整數(shù)集合中隨機返回一個元素。 調(diào)用 dictGetRandomKey 函數(shù)徒像, 從字典中隨機返回一個字典鍵黍特。
SPOP 調(diào)用 intsetRandom 函數(shù), 從整數(shù)集合中隨機取出一個元素锯蛀, 在將這個隨機元素返回給客戶端之后灭衷, 調(diào)用 intsetRemove 函數(shù), 將隨機元素從整數(shù)集合中刪除掉旁涤。 調(diào)用 dictGetRandomKey 函數(shù)翔曲, 從字典中隨機取出一個字典鍵, 在將這個隨機字典鍵的值返回給客戶端之后劈愚, 調(diào)用 dictDelete 函數(shù)瞳遍, 從字典中刪除隨機字典鍵所對應(yīng)的鍵值對。
SREM 調(diào)用 intsetRemove 函數(shù)菌羽, 從整數(shù)集合中刪除所有給定的元素掠械。 調(diào)用 dictDelete 函數(shù), 從字典中刪除所有鍵為給定元素的鍵值對注祖。

有序集合對象

有序集合的編碼可以是 ziplist 或者 skiplist猾蒂,即跳躍表或者壓縮列表。跟哈希對象使用壓縮列表實現(xiàn)類似是晨,每個有序集合元素使用相連的兩個壓縮列表節(jié)點分別表示成員和分值婚夫,分值按從小到大的順序排序。

而使用跳躍表實現(xiàn)的有序集合底層使用了zset結(jié)構(gòu)署鸡,zset包含了一個跳躍表和一個字典:

typedef struct zset {

    zskiplist *zsl;

    dict *dict;

} zset;

zset 結(jié)構(gòu)中的 zsl 跳躍表按分值從小到大保存了所有集合元素案糙, 每個跳躍表節(jié)點都保存了一個集合元素: 跳躍表節(jié)點的 object 屬性保存了元素的成員, 而跳躍表節(jié)點的 score 屬性則保存了元素的分值靴庆。

zset 結(jié)構(gòu)中的 dict 字典為有序集合創(chuàng)建了一個從成員到分值的映射时捌, 字典中的每個鍵值對都保存了一個集合元素: 字典的鍵保存了元素的成員, 而字典的值則保存了元素的分值炉抒。

同時使用兩個數(shù)據(jù)結(jié)構(gòu)綜合了兩種結(jié)構(gòu)的優(yōu)點奢讨,通過跳躍表能夠方便的執(zhí)行范圍查詢,而通過字典能夠方便的進行元素分數(shù)查詢焰薄。

有序集合每個元素的成員都是一個字符串對象拿诸, 而每個元素的分值都是一個 double 類型的浮點數(shù)扒袖。跳躍表和字典底層通過指針會共享這些對象,所以不會浪費額外的內(nèi)存亩码。

當(dāng)有序集合對象可以同時滿足以下兩個條件時季率,對象使用 ziplist 編碼,可通過配置文件設(shè)置限制值:

  • 有序集合保存的元素數(shù)量小于 128 個描沟;
  • 有序集合保存的所有元素成員的長度都小于 64 字節(jié)飒泻;

有序集合命令的實現(xiàn)集合:

命令 ziplist 編碼的實現(xiàn)方法 zset 編碼的實現(xiàn)方法
ZADD 調(diào)用 ziplistInsert 函數(shù), 將成員和分值作為兩個節(jié)點分別插入到壓縮列表吏廉。 先調(diào)用 zslInsert 函數(shù)泞遗, 將新元素添加到跳躍表席覆, 然后調(diào)用 dictAdd 函數(shù)畦戒, 將新元素關(guān)聯(lián)到字典垃环。
ZCARD 調(diào)用 ziplistLen 函數(shù), 獲得壓縮列表包含節(jié)點的數(shù)量霹肝, 將這個數(shù)量除以 2 得出集合元素的數(shù)量。 訪問跳躍表數(shù)據(jù)結(jié)構(gòu)的 length 屬性系枪, 直接返回集合元素的數(shù)量。
ZCOUNT 遍歷壓縮列表宏榕, 統(tǒng)計分值在給定范圍內(nèi)的節(jié)點的數(shù)量。 遍歷跳躍表抚芦, 統(tǒng)計分值在給定范圍內(nèi)的節(jié)點的數(shù)量倍谜。
ZRANGE 從表頭向表尾遍歷壓縮列表, 返回給定索引范圍內(nèi)的所有元素叉抡。 從表頭向表尾遍歷跳躍表尔崔, 返回給定索引范圍內(nèi)的所有元素。
ZREVRANGE 從表尾向表頭遍歷壓縮列表褥民, 返回給定索引范圍內(nèi)的所有元素季春。 從表尾向表頭遍歷跳躍表, 返回給定索引范圍內(nèi)的所有元素消返。
ZRANK 從表頭向表尾遍歷壓縮列表载弄, 查找給定的成員, 沿途記錄經(jīng)過節(jié)點的數(shù)量撵颊, 當(dāng)找到給定成員之后侦锯, 途經(jīng)節(jié)點的數(shù)量就是該成員所對應(yīng)元素的排名。 從表頭向表尾遍歷跳躍表秦驯, 查找給定的成員尺碰, 沿途記錄經(jīng)過節(jié)點的數(shù)量, 當(dāng)找到給定成員之后, 途經(jīng)節(jié)點的數(shù)量就是該成員所對應(yīng)元素的排名亲桥。
ZREVRANK 從表尾向表頭遍歷壓縮列表洛心, 查找給定的成員, 沿途記錄經(jīng)過節(jié)點的數(shù)量题篷, 當(dāng)找到給定成員之后词身, 途經(jīng)節(jié)點的數(shù)量就是該成員所對應(yīng)元素的排名。 從表尾向表頭遍歷跳躍表番枚, 查找給定的成員法严, 沿途記錄經(jīng)過節(jié)點的數(shù)量, 當(dāng)找到給定成員之后葫笼, 途經(jīng)節(jié)點的數(shù)量就是該成員所對應(yīng)元素的排名深啤。
ZREM 遍歷壓縮列表, 刪除所有包含給定成員的節(jié)點路星, 以及被刪除成員節(jié)點旁邊的分值節(jié)點溯街。 遍歷跳躍表, 刪除所有包含了給定成員的跳躍表節(jié)點洋丐。 并在字典中解除被刪除元素的成員和分值的關(guān)聯(lián)呈昔。
ZSCORE 遍歷壓縮列表, 查找包含了給定成員的節(jié)點友绝, 然后取出成員節(jié)點旁邊的分值節(jié)點保存的元素分值堤尾。 直接從字典中取出給定成員的分值。

類型檢查和命令多態(tài)

redis中的命令分為兩種:對所有類型的鍵都可以執(zhí)行以及只能對特定的鍵執(zhí)行迁客,而只能對特性鍵執(zhí)行的命令需要進行鍵的類型檢查哀峻。

回顧一下redisObject機構(gòu),其中有個屬性是type哲泊,redis在執(zhí)行類型特定命令前就是通過這個type屬性來判斷能夠繼續(xù)執(zhí)行的剩蟀。

對于某種redis對象來說,底層會有多種實現(xiàn)切威,比如列表對象就可以有壓縮列表和雙端鏈表兩種實現(xiàn)育特,而對于同一個命令,不同的實現(xiàn)應(yīng)該都能夠響應(yīng)先朦,針對不同的數(shù)據(jù)結(jié)構(gòu)來做不同的處理缰冤,這就是命令的多態(tài)。

內(nèi)存回收

C語言不像Java擁有虛擬機和垃圾回收器喳魏,所以redis自己實現(xiàn)了一個基于引用計數(shù)的內(nèi)存回收機制棉浸。每個redisObject里都有一個refcount的屬性,這個屬性值會隨著對象的使用狀態(tài)不同而不斷變化:

  • 在創(chuàng)建一個新對象時刺彩, 引用計數(shù)的值會被初始化為 1 迷郑;
  • 當(dāng)對象被一個新程序使用時枝恋, 它的引用計數(shù)值會被增一;
  • 當(dāng)對象不再被一個程序使用時嗡害, 它的引用計數(shù)值會被減一焚碌;
  • 當(dāng)對象的引用計數(shù)值變?yōu)?0 時, 對象所占用的內(nèi)存會被釋放霸妹。

對象共享

對于不同的鍵引用了相同的值對象十电,redis實現(xiàn)了對象共享機制,通過將指針指向相同的對象來實現(xiàn)內(nèi)存的高效利用叹螟。同時refcount屬性記錄了引用的數(shù)量鹃骂。

默認redis會在初始化服務(wù)器的時候就創(chuàng)建一萬個字符串對象,包含從0到9999的所有整數(shù)值罢绽。

redis實現(xiàn)對象共享只針對于整數(shù)型的字符串對象畏线,原因如下:

  • 如果共享對象是保存整數(shù)值的字符串對象, 那么驗證操作的復(fù)雜度為 O(1) 有缆;
  • 如果共享對象是保存字符串值的字符串對象象踊, 那么驗證操作的復(fù)雜度為 O(N) 温亲;
  • 如果共享對象是包含了多個值(或者對象的)對象棚壁, 比如列表對象或者哈希對象, 那么驗證操作的復(fù)雜度將會是 O(N^2) 栈虚。

因此袖外, 盡管共享更復(fù)雜的對象可以節(jié)約更多的內(nèi)存, 但受到 CPU 時間的限制魂务, Redis 只對包含整數(shù)值的字符串對象進行共享曼验。

對象的空轉(zhuǎn)時長

對象的空轉(zhuǎn)時長可以通過redisObject中的lru屬性實現(xiàn),OBJECT IDLETIME 命令可以通過將當(dāng)前時間減去lru的值來計算對象的空轉(zhuǎn)時長粘姜。并且使用 OBJECT IDLETIME 命令并不會更新對象的lru的值鬓照。

對象的lru還被用于實現(xiàn)redis的特定內(nèi)存淘汰策略,這里就不詳述了孤紧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豺裆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子号显,更是在濱河造成了極大的恐慌臭猜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件押蚤,死亡現(xiàn)場離奇詭異蔑歌,居然都是意外死亡,警方通過查閱死者的電腦和手機揽碘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門次屠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來园匹,“玉大人,你說我怎么就攤上這事帅矗≠怂啵” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵浑此,是天一觀的道長累颂。 經(jīng)常有香客問我,道長凛俱,這世上最難降的妖魔是什么紊馏? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蒲犬,結(jié)果婚禮上朱监,老公的妹妹穿的比我還像新娘。我一直安慰自己原叮,他們只是感情好赫编,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奋隶,像睡著了一般擂送。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唯欣,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天嘹吨,我揣著相機與錄音,去河邊找鬼境氢。 笑死蟀拷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萍聊。 我是一名探鬼主播问芬,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寿桨!你這毒婦竟也來了此衅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牛隅,失蹤者是張志新(化名)和其女友劉穎炕柔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媒佣,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡匕累,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了默伍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欢嘿。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡衰琐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炼蹦,到底是詐尸還是另有隱情羡宙,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布掐隐,位于F島的核電站狗热,受9級特大地震影響躯喇,放射性物質(zhì)發(fā)生泄漏肛度。R本人自食惡果不足惜好港,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一古拴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧礼患,春花似錦刺洒、人聲如沸装获。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怀大,卻和暖如春纱兑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叉寂。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工萍启, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留总珠,地道東北人屏鳍。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像局服,于是被迫代替她去往敵國和親钓瞭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Redis的優(yōu)點之一就是支持豐富的對象淫奔,這個對象系統(tǒng)包含字符串對象山涡、列表對象、哈希對象唆迁、集合對象和有序集合對象鸭丛。本...
    wenmingxing閱讀 780評論 0 3
  • redis的對象包含5種對象: 字符串對象 列表對象 哈希對象 集合對象 有序集合對象 redis對象的好處 針對...
    BlackChen閱讀 720評論 0 1
  • 轉(zhuǎn)載:可能是目前最詳細的Redis內(nèi)存模型及應(yīng)用解讀 Redis是目前最火爆的內(nèi)存數(shù)據(jù)庫之一,通過在內(nèi)存中讀寫數(shù)據(jù)...
    meng_philip123閱讀 1,440評論 1 22
  • 前言 Redis是目前最火爆的內(nèi)存數(shù)據(jù)庫之一唐责,通過在內(nèi)存中讀寫數(shù)據(jù)鳞溉,大大提高了讀寫速度,可以說Redis是實現(xiàn)網(wǎng)站...
    小陳阿飛閱讀 805評論 0 1
  • 文章摘錄: 概念:柯伊伯帶鼠哥、奧爾特云熟菲、比鄰星看政、紅矮星、宜居帶抄罕、本星系群允蚣、星系團、臨界最后反射面呆贿、宇宙的第一原則嚷兔,第...
    肚子的遐想閱讀 634評論 0 0