Redis 對(duì)象的類型與編碼

2018-08-02 14:48 更新

Redis 使用對(duì)象來表示數(shù)據(jù)庫(kù)中的鍵和值, 每次當(dāng)我們?cè)?Redis 的數(shù)據(jù)庫(kù)中新創(chuàng)建一個(gè)鍵值對(duì)時(shí), 我們至少會(huì)創(chuàng)建兩個(gè)對(duì)象, 一個(gè)對(duì)象用作鍵值對(duì)的鍵(鍵對(duì)象), 另一個(gè)對(duì)象用作鍵值對(duì)的值(值對(duì)象)。

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

redis> SET msg "hello world"
OK

Redis 中的每個(gè)對(duì)象都由一個(gè) redisObject 結(jié)構(gòu)表示, 該結(jié)構(gòu)中和保存數(shù)據(jù)有關(guān)的三個(gè)屬性分別是 type 屬性、 encoding 屬性和 ptr 屬性:

typedef struct redisObject {

    // 類型
    unsigned type:4;

    // 編碼
    unsigned encoding:4;

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

    // ...

} robj;

類型

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


表 8-1 對(duì)象的類型

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

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

  • 當(dāng)我們稱呼一個(gè)數(shù)據(jù)庫(kù)鍵為“字符串鍵”時(shí), 我們指的是“這個(gè)數(shù)據(jù)庫(kù)鍵所對(duì)應(yīng)的值為字符串對(duì)象”;
  • 當(dāng)我們稱呼一個(gè)鍵為“列表鍵”時(shí), 我們指的是“這個(gè)數(shù)據(jù)庫(kù)鍵所對(duì)應(yīng)的值為列表對(duì)象”,

諸如此類。

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

# 鍵為字符串對(duì)象,值為字符串對(duì)象

redis> SET msg "hello world"
OK

redis> TYPE msg
string

# 鍵為字符串對(duì)象,值為列表對(duì)象

redis> RPUSH numbers 1 3 5
(integer) 6

redis> TYPE numbers
list

# 鍵為字符串對(duì)象,值為哈希對(duì)象

redis> HMSET profile name Tome age 25 career Programmer
OK

redis> TYPE profile
hash

# 鍵為字符串對(duì)象,值為集合對(duì)象

redis> SADD fruits apple banana cherry
(integer) 3

redis> TYPE fruits
set

# 鍵為字符串對(duì)象,值為有序集合對(duì)象

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

redis> TYPE price
zset

表 8-2 列出了 TYPE 命令在面對(duì)不同類型的值對(duì)象時(shí)所產(chǎn)生的輸出。


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

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

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

對(duì)象的 ptr 指針指向?qū)ο蟮牡讓訉?shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu), 而這些數(shù)據(jù)結(jié)構(gòu)由對(duì)象的 encoding 屬性決定。

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


表 8-3 對(duì)象的編碼

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

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


表 8-4 不同類型和編碼的對(duì)象

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

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

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 列出了不同編碼的對(duì)象所對(duì)應(yīng)的 OBJECT ENCODING 命令輸出。


表 8-5 OBJECT ENCODING 對(duì)不同編碼的輸出

對(duì)象所使用的底層數(shù)據(jù)結(jié)構(gòu) 編碼常量 OBJECT ENCODING 命令輸出
整數(shù) REDIS_ENCODING_INT "int"
embstr 編碼的簡(jiǎn)單動(dòng)態(tài)字符串(SDS) REDIS_ENCODING_EMBSTR "embstr"
簡(jiǎn)單動(dòng)態(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 屬性來設(shè)定對(duì)象所使用的編碼, 而不是為特定類型的對(duì)象關(guān)聯(lián)一種固定的編碼, 極大地提升了 Redis 的靈活性和效率, 因?yàn)?Redis 可以根據(jù)不同的使用場(chǎng)景來為一個(gè)對(duì)象設(shè)置不同的編碼, 從而優(yōu)化對(duì)象在某一場(chǎng)景下的效率。

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

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

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

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

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)