国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Redis 對象處理機制

2018-08-02 11:48 更新

對象處理機制

在 Redis 的命令中,用于對鍵(key)進行處理的命令占了很大一部分,而對于鍵所保存的值的類型(后簡稱“鍵的類型”),鍵能執(zhí)行的命令又各不相同。

比如說,LPUSHLLEN 只能用于列表鍵,而 SADDSRANDMEMBER 只能用于集合鍵,等等。

另外一些命令,比如 DELTTLTYPE ,可以用于任何類型的鍵,但是,要正確實現(xiàn)這些命令,必須為不同類型的鍵設(shè)置不同的處理方式:比如說,刪除一個列表鍵和刪除一個字符串鍵的操作過程就不太一樣。

以上的描述說明,Redis 必須讓每個鍵都帶有類型信息,使得程序可以檢查鍵的類型,并為它選擇合適的處理方式。

另外,在前面介紹各個底層數(shù)據(jù)結(jié)構(gòu)時有提到,Redis 的每一種數(shù)據(jù)類型,比如字符串、列表、有序集,它們都擁有不只一種底層實現(xiàn)(Redis 內(nèi)部稱之為編碼,encoding),這說明,每當(dāng)對某種數(shù)據(jù)類型的鍵進行操作時,程序都必須根據(jù)鍵所采取的編碼,進行不同的操作。

比如說,集合類型就可以由字典和整數(shù)集合兩種不同的數(shù)據(jù)結(jié)構(gòu)實現(xiàn),但是,當(dāng)用戶執(zhí)行 ZADD 命令時,他/她應(yīng)該不必關(guān)心集合使用的是什么編碼,只要 Redis 能按照 ZADD 命令的指示,將新元素添加到集合就可以了。

這說明,操作數(shù)據(jù)類型的命令除了要對鍵的類型進行檢查之外,還需要根據(jù)數(shù)據(jù)類型的不同編碼進行多態(tài)處理。

為了解決以上問題,Redis 構(gòu)建了自己的類型系統(tǒng),這個系統(tǒng)的主要功能包括:

  • redisObject 對象。
  • 基于 redisObject 對象的類型檢查。
  • 基于 redisObject 對象的顯式多態(tài)函數(shù)。
  • redisObject 進行分配、共享和銷毀的機制。

以下小節(jié)將分別介紹類型系統(tǒng)的這幾個方面。

Note

因為 C 并不是面向?qū)ο笳Z言,這里將 redisObject 稱呼為對象一是為了講述的方便,二是希望通過模仿 OOP 的常用術(shù)語,讓這里的內(nèi)容更容易被理解,redisObject 實際上是只是一個結(jié)構(gòu)類型。

redisObject 數(shù)據(jù)結(jié)構(gòu),以及 Redis 的數(shù)據(jù)類型

redisObject 是 Redis 類型系統(tǒng)的核心,數(shù)據(jù)庫中的每個鍵、值,以及 Redis 本身處理的參數(shù),都表示為這種數(shù)據(jù)類型。

redisObject 的定義位于 redis.h

/*
 * Redis 對象
 */
typedef struct redisObject {

    // 類型
    unsigned type:4;

    // 對齊位
    unsigned notused:2;

    // 編碼方式
    unsigned encoding:4;

    // LRU 時間(相對于 server.lruclock)
    unsigned lru:22;

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

    // 指向?qū)ο蟮闹?    void *ptr;

} robj;

type 、 encodingptr 是最重要的三個屬性。

type 記錄了對象所保存的值的類型,它的值可能是以下常量的其中一個(定義位于 redis.h):

/*
 * 對象類型
 */
#define REDIS_STRING 0  // 字符串
#define REDIS_LIST 1    // 列表
#define REDIS_SET 2     // 集合
#define REDIS_ZSET 3    // 有序集
#define REDIS_HASH 4    // 哈希表

encoding 記錄了對象所保存的值的編碼,它的值可能是以下常量的其中一個(定義位于 redis.h):

/*
 * 對象編碼
 */
#define REDIS_ENCODING_RAW 0            // 編碼為字符串
#define REDIS_ENCODING_INT 1            // 編碼為整數(shù)
#define REDIS_ENCODING_HT 2             // 編碼為哈希表
#define REDIS_ENCODING_ZIPMAP 3         // 編碼為 zipmap
#define REDIS_ENCODING_LINKEDLIST 4     // 編碼為雙端鏈表
#define REDIS_ENCODING_ZIPLIST 5        // 編碼為壓縮列表
#define REDIS_ENCODING_INTSET 6         // 編碼為整數(shù)集合
#define REDIS_ENCODING_SKIPLIST 7       // 編碼為跳躍表

ptr 是一個指針,指向?qū)嶋H保存值的數(shù)據(jù)結(jié)構(gòu),這個數(shù)據(jù)結(jié)構(gòu)由 type 屬性和 encoding 屬性決定。

舉個例子,如果一個 redisObjecttype 屬性為 REDIS_LIST , encoding 屬性為 REDIS_ENCODING_LINKEDLIST ,那么這個對象就是一個 Redis 列表,它的值保存在一個雙端鏈表內(nèi),而 ptr 指針就指向這個雙端鏈表;

另一方面,如果一個 redisObjecttype 屬性為 REDIS_HASH , encoding 屬性為 REDIS_ENCODING_ZIPMAP ,那么這個對象就是一個 Redis 哈希表,它的值保存在一個 zipmap 里,而 ptr 指針就指向這個 zipmap ;諸如此類。

下圖展示了 redisObject 、Redis 所有數(shù)據(jù)類型、以及 Redis 所有編碼方式(底層實現(xiàn))三者之間的關(guān)系:

digraph datatype {    rankdir=LR;    node[shape=plaintext, style = filled];    edge [style = bold];    // obj    redisObject [label= REDIS_STRING; redisObject -> REDIS_LIST; redisObject -> REDIS_SET; redisObject -> REDIS_ZSET; redisObject -> REDIS_HASH; REDIS_STRING -> REDIS_ENCODING_RAW; REDIS_STRING -> REDIS_ENCODING_INT; REDIS_LIST -> REDIS_ENCODING_LINKEDLIST; REDIS_LIST -> REDIS_ENCODING_ZIPLIST; REDIS_SET -> REDIS_ENCODING_HT; REDIS_SET -> REDIS_ENCODING_INTSET; REDIS_ZSET -> REDIS_ENCODING_SKIPLIST; REDIS_ZSET -> REDIS_ENCODING_ZIPLIST; REDIS_HASH -> REDIS_ENCODING_HT; REDIS_HASH -> REDIS_ENCODING_ZIPLIST;}" />

這個圖展示了 Redis 各種數(shù)據(jù)類型,以及它們的編碼方式。

Note

REDIS_ENCODING_ZIPMAP 沒有出現(xiàn)在圖中,因為從 Redis 2.6 開始,它不再是任何數(shù)據(jù)類型的底層結(jié)構(gòu)。

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

有了 redisObject 結(jié)構(gòu)的存在,在執(zhí)行處理數(shù)據(jù)類型的命令時,進行類型檢查和對編碼進行多態(tài)操作就簡單得多了。

當(dāng)執(zhí)行一個處理數(shù)據(jù)類型的命令時,Redis 執(zhí)行以下步驟:

  1. 根據(jù)給定 key ,在數(shù)據(jù)庫字典中查找和它相對應(yīng)的 redisObject ,如果沒找到,就返回 NULL 。
  2. 檢查 redisObjecttype 屬性和執(zhí)行命令所需的類型是否相符,如果不相符,返回類型錯誤。
  3. 根據(jù) redisObjectencoding 屬性所指定的編碼,選擇合適的操作函數(shù)來處理底層的數(shù)據(jù)結(jié)構(gòu)。
  4. 返回數(shù)據(jù)結(jié)構(gòu)的操作結(jié)果作為命令的返回值。

作為例子,以下展示了對鍵 key 執(zhí)行 LPOP 命令的完整過程:

digraph command_poly {    node [shape=plaintext, style = filled];    edge [style = bold];    lpop [label= get_key_obj_from_db; get_key_obj_from_db -> is_obj_nil_or_not; is_obj_nil_or_not -> return_nil [label="是"]; is_obj_nil_or_not -> is_type_list_or_not [label="否"]; is_type_list_or_not -> call_poly_pop_function [label="是"]; is_type_list_or_not -> return_type_error [label="否"]; call_poly_pop_function -> pop_from_ziplist [label="對象的編碼為\nZIPLIST"]; call_poly_pop_function -> pop_from_linkedlist [label="對象的編碼為\nLINKEDLIST"]; pop_from_ziplist -> return_pop_item; pop_from_linkedlist -> return_pop_item;}" />

對象共享

有一些對象在 Redis 中非常常見,比如命令的返回值 OK 、 ERRORWRONGTYPE 等字符,另外,一些小范圍的整數(shù),比如個位、十位、百位的整數(shù)都非常常見。

為了利用這種常見情況,Redis 在內(nèi)部使用了一個 Flyweight 模式 :通過預(yù)分配一些常見的值對象,并在多個數(shù)據(jù)結(jié)構(gòu)之間共享這些對象,程序避免了重復(fù)分配的麻煩,也節(jié)約了一些 CPU 時間。

Redis 預(yù)分配的值對象有如下這些:

  • 各種命令的返回值,比如執(zhí)行成功時返回的 OK ,執(zhí)行錯誤時返回的 ERROR ,類型錯誤時返回的 WRONGTYPE ,命令入隊事務(wù)時返回的 QUEUED ,等等。
  • 包括 0 在內(nèi),小于 redis.h/REDIS_SHARED_INTEGERS 的所有整數(shù)(REDIS_SHARED_INTEGERS 的默認(rèn)值為 10000

因為命令的回復(fù)值直接返回給客戶端,所以它們的值無須進行共享;另一方面,如果某個命令的輸入值是一個小于 REDIS_SHARED_INTEGERS 的整數(shù)對象,那么當(dāng)這個對象要被保存進數(shù)據(jù)庫時,Redis 就會釋放原來的值,并將值的指針指向共享對象。

作為例子,下圖展示了三個列表,它們都帶有指向共享對象數(shù)組中某個值對象的指針:

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號