Redis 對象的類型與編碼

2018-08-02 14:48 更新

Redis 使用對象來表示數(shù)據(jù)庫中的鍵和值, 每次當我們在 Redis 的數(shù)據(jù)庫中新創(chuàng)建一個鍵值對時, 我們至少會創(chuàng)建兩個對象, 一個對象用作鍵值對的鍵(鍵對象), 另一個對象用作鍵值對的值(值對象)。

舉個例子, 以下 SET 命令在數(shù)據(jù)庫中創(chuàng)建了一個新的鍵值對, 其中鍵值對的鍵是一個包含了字符串值 "msg" 的對象, 而鍵值對的值則是一個包含了字符串值 "hello world" 的對象:

redis> SET msg "hello world"
OK

Redis 中的每個對象都由一個 redisObject 結構表示, 該結構中和保存數(shù)據(jù)有關的三個屬性分別是 type 屬性、 encoding 屬性和 ptr 屬性:

typedef struct redisObject {

    // 類型
    unsigned type:4;

    // 編碼
    unsigned encoding:4;

    // 指向底層實現(xiàn)數(shù)據(jù)結構的指針
    void *ptr;

    // ...

} robj;

類型

對象的 type 屬性記錄了對象的類型, 這個屬性的值可以是表 8-1 列出的常量的其中一個。


表 8-1 對象的類型

類型常量 對象的名稱
REDIS_STRING 字符串對象
REDIS_LIST 列表對象
REDIS_HASH 哈希對象
REDIS_SET 集合對象
REDIS_ZSET 有序集合對象

對于 Redis 數(shù)據(jù)庫保存的鍵值對來說, 鍵總是一個字符串對象, 而值則可以是字符串對象、列表對象、哈希對象、集合對象或者有序集合對象的其中一種, 因此:

  • 當我們稱呼一個數(shù)據(jù)庫鍵為“字符串鍵”時, 我們指的是“這個數(shù)據(jù)庫鍵所對應的值為字符串對象”;
  • 當我們稱呼一個鍵為“列表鍵”時, 我們指的是“這個數(shù)據(jù)庫鍵所對應的值為列表對象”,

諸如此類。

TYPE 命令的實現(xiàn)方式也與此類似, 當我們對一個數(shù)據(jù)庫鍵執(zhí)行 TYPE 命令時, 命令返回的結果為數(shù)據(jù)庫鍵對應的值對象的類型, 而不是鍵對象的類型:

# 鍵為字符串對象,值為字符串對象

redis> SET msg "hello world"
OK

redis> TYPE msg
string

# 鍵為字符串對象,值為列表對象

redis> RPUSH numbers 1 3 5
(integer) 6

redis> TYPE numbers
list

# 鍵為字符串對象,值為哈希對象

redis> HMSET profile name Tome age 25 career Programmer
OK

redis> TYPE profile
hash

# 鍵為字符串對象,值為集合對象

redis> SADD fruits apple banana cherry
(integer) 3

redis> TYPE fruits
set

# 鍵為字符串對象,值為有序集合對象

redis> ZADD price 8.5 apple 5.0 banana 6.0 cherry
(integer) 3

redis> TYPE price
zset

表 8-2 列出了 TYPE 命令在面對不同類型的值對象時所產生的輸出。


表 8-2 不同類型值對象的 TYPE 命令輸出

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

編碼和底層實現(xiàn)

對象的 ptr 指針指向對象的底層實現(xiàn)數(shù)據(jù)結構, 而這些數(shù)據(jù)結構由對象的 encoding 屬性決定。

encoding 屬性記錄了對象所使用的編碼, 也即是說這個對象使用了什么數(shù)據(jù)結構作為對象的底層實現(xiàn), 這個屬性的值可以是表 8-3 列出的常量的其中一個。


表 8-3 對象的編碼

編碼常量 編碼所對應的底層數(shù)據(jù)結構
REDIS_ENCODING_INT long 類型的整數(shù)
REDIS_ENCODING_EMBSTR embstr 編碼的簡單動態(tài)字符串
REDIS_ENCODING_RAW 簡單動態(tài)字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 雙端鏈表
REDIS_ENCODING_ZIPLIST 壓縮列表
REDIS_ENCODING_INTSET 整數(shù)集合
REDIS_ENCODING_SKIPLIST 跳躍表和字典

每種類型的對象都至少使用了兩種不同的編碼, 表 8-4 列出了每種類型的對象可以使用的編碼。


表 8-4 不同類型和編碼的對象

類型 編碼 對象
REDIS_STRING REDIS_ENCODING_INT 使用整數(shù)值實現(xiàn)的字符串對象。
REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 編碼的簡單動態(tài)字符串實現(xiàn)的字符串對象。
REDIS_STRING REDIS_ENCODING_RAW 使用簡單動態(tài)字符串實現(xiàn)的字符串對象。
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的列表對象。
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用雙端鏈表實現(xiàn)的列表對象。
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的哈希對象。
REDIS_HASH REDIS_ENCODING_HT 使用字典實現(xiàn)的哈希對象。
REDIS_SET REDIS_ENCODING_INTSET 使用整數(shù)集合實現(xiàn)的集合對象。
REDIS_SET REDIS_ENCODING_HT 使用字典實現(xiàn)的集合對象。
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用壓縮列表實現(xiàn)的有序集合對象。
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳躍表和字典實現(xiàn)的有序集合對象。

使用 OBJECT ENCODING 命令可以查看一個數(shù)據(jù)庫鍵的值對象的編碼:

redis> SET msg "hello wrold"
OK

redis> OBJECT ENCODING msg
"embstr"

redis> SET story "long long long long long long ago ..."
OK

redis> OBJECT ENCODING story
"raw"

redis> SADD numbers 1 3 5
(integer) 3

redis> OBJECT ENCODING numbers
"intset"

redis> SADD numbers "seven"
(integer) 1

redis> OBJECT ENCODING numbers
"hashtable"

表 8-5 列出了不同編碼的對象所對應的 OBJECT ENCODING 命令輸出。


表 8-5 OBJECT ENCODING 對不同編碼的輸出

對象所使用的底層數(shù)據(jù)結構 編碼常量 OBJECT ENCODING 命令輸出
整數(shù) REDIS_ENCODING_INT "int"
embstr 編碼的簡單動態(tài)字符串(SDS) REDIS_ENCODING_EMBSTR "embstr"
簡單動態(tài)字符串 REDIS_ENCODING_RAW "raw"
字典 REDIS_ENCODING_HT "hashtable"
雙端鏈表 REDIS_ENCODING_LINKEDLIST "linkedlist"
壓縮列表 REDIS_ENCODING_ZIPLIST "ziplist"
整數(shù)集合 REDIS_ENCODING_INTSET "intset"
跳躍表和字典 REDIS_ENCODING_SKIPLIST "skiplist"

通過 encoding 屬性來設定對象所使用的編碼, 而不是為特定類型的對象關聯(lián)一種固定的編碼, 極大地提升了 Redis 的靈活性和效率, 因為 Redis 可以根據(jù)不同的使用場景來為一個對象設置不同的編碼, 從而優(yōu)化對象在某一場景下的效率。

舉個例子, 在列表對象包含的元素比較少時, Redis 使用壓縮列表作為列表對象的底層實現(xiàn):

  • 因為壓縮列表比雙端鏈表更節(jié)約內存, 并且在元素數(shù)量較少時, 在內存中以連續(xù)塊方式保存的壓縮列表比起雙端鏈表可以更快被載入到緩存中;
  • 隨著列表對象包含的元素越來越多, 使用壓縮列表來保存元素的優(yōu)勢逐漸消失時, 對象就會將底層實現(xiàn)從壓縮列表轉向功能更強、也更適合保存大量元素的雙端鏈表上面;

其他類型的對象也會通過使用多種不同的編碼來進行類似的優(yōu)化。

在接下來的內容中, 我們將分別介紹 Redis 中的五種不同類型的對象, 說明這些對象底層所使用的編碼方式, 列出對象從一種編碼轉換成另一種編碼所需的條件, 以及同一個命令在多種不同編碼上的實現(xiàn)方法。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號