首頁>技術>

五大資料型別都會封裝成 RedisObject 。

typedef struct redisObject {    unsigned type:4; // 型別    unsigned encoding:4; // 編碼    // ...    void *ptr; // 指向具體底層資料的指標} robj;

不同資料型別的主要區別就是 type 和 encoding 屬性的差異,同一種資料型別,有不同的編碼。

一、編碼型別

字串的編碼有 raw 、 embstr 、 int 三種。

rawembstrint

定義在 server.h 中,這裡只列出 string 型別的編碼

#define OBJ_ENCODING_RAW 0#define OBJ_ENCODING_INT 1#define OBJ_ENCODING_EMBSTR 8

編碼 1:raw

raw 編碼主要用來儲存長度超過 44 的字串。其真實資料,由 sdshdr 結構來表示儲存,外層還是由 redisObject 包裝。

sdshdr 的結構在前文 Redis 設計與實現 3:字串 SDS 中有講到。

sdshdr 結構大致如下:

redisObject 中的 ptr 指標,就是指向 sds 。

編碼 2:embstr

embstr 編碼是專門用於儲存 短字串 的一種最佳化編碼方式。當字串的長度小於等於 44 的時候,將採用 embstr 編碼。

建立字串物件的程式碼如下( object.c ):

#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44robj *createStringObject(const char *ptr, size_t len) {    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)        return createEmbeddedStringObject(ptr,len);    else        return createRawStringObject(ptr,len);}

embstr 有個顯著的特點,就是 redisObject 跟 sds 的記憶體是挨在一起的。挨在一起的好處:

分配記憶體的時候,只需要分配一次。而 raw 編碼的 sds 跟 redisObject 分離,就要分配兩次記憶體。同樣,釋放記憶體也只需要釋放一次。連續記憶體能更好利用記憶體帶來的優勢。

embstr 問題一:那麼為什麼 embstr 跟 raw 的界限是44呢?

embstr 的 sds 使用了 sdshdr8 , sdshdr8 頭佔用了 3 個位元組:
struct __attribute__ ((__packed__)) sdshdr8 {    uint8_t len; /* 1 位元組 */    uint8_t alloc; /* 1 位元組 */    unsigned char flags; /* 1 位元組 */    char buf[];};
另外還有 redisObject 佔用 16 個位元組 ( 4 + 4 + 24 + 32 + 64 = 128 位):
typedef struct redisObject {    unsigned type:4;    unsigned encoding:4;    unsigned lru:LRU_BITS; // #define LRU_BITS 24    int refcount; // 32 位    void *ptr; // 64 位} robj;

redisObject + sdshdr8 至少需要 3 + 16 = 19 位元組。

redis 認為如果超過 64 位元組就是大字串,所以在 redisObject+ sdshdr8 的總長度是 64 位元組的情況下,留給 buf 的長度就只剩下 45 位元組,由於字串結尾需要一個 \0 佔用一個位元組,所以留個字串的長度就只有 44 位元組了。

公式: 64 - 3(sdshdr8 ) - 16(redisObject) - 1(\0) = 44

embstr 問題二:為什麼網上有的博文說 embstr 跟 raw 的界限是 39

在 redis 3.2 版本之前,這個界限的確是 39,為什麼後面改成 44 了呢?

那是因為 sdshdr 的結構在 3.2 版本的時候修改了。3.2 之前的 sdshdr 結構是:

struct sdshdr {    unsigned int len; // 4 位元組    unsigned int free; // 4 位元組    char buf[];};

舊版本的 sdshdr 的頭佔用了 8 個位元組,比新版本的多了 5 個位元組,所以界限就是 44 - 5 = 39 啦!

編碼 3:int

如果一個字串物件儲存的是整數值,並且這個整數值可以用 long 型別來表示,那麼這個整數值將會儲存在字串物件結構的 ptr 屬性裡面(將 void* 轉換成 long ),並將字串物件的編碼設定為 int 。

相對於用 raw 編碼, int 編碼既節省了指標佔用的記憶體,也節省了 sds 結構的記憶體。

redis> SET int_key 12345OKredis> OBJECT ENCODING int_key"int"

下圖為存著 12345 的 string 示例結構:

二、編碼的轉換

1. int 轉 raw

當字串傳的不是整數的時候,int 就會轉成 raw 編碼。如果執行了一些修改的命令,如 append 等( set 不算),都會轉成 raw 編碼。因為這些操作只有字串才支援。一旦編碼變為 raw 之後,將不會再轉成 embstr
127.0.0.1:6379> SET num 1OK127.0.0.1:6379> OBJECT ENCODING num"int"127.0.0.1:6379> APPEND num 2(integer) 2127.0.0.1:6379> OBJECT ENCODING num"raw"127.0.0.1:6379> SET num 12OK127.0.0.1:6379> OBJECT ENCODING num"int"

2. embstr 轉 raw

如果執行了一些修改的命令,如 append 等,都會轉成 raw 編碼,不管修改後字串的長度。因為沒有給 embstr 編碼實現修改介面,所以實際上 embsr 是隻讀的。一旦編碼變為 raw 之後,將不會再轉成 embstr三、重點回顧字串物件有三種編碼, raw 、 embstr 、 intraw 負責儲存長字串; embstr 負責儲存短字串; int 負責儲存整數。int 和 embstr 在修改的時候,會轉成 raw 編碼,並且不再轉回

希望可以對你們學習Redis有幫助,喜歡的小夥伴可以轉發+關注一下,感謝支援!

原文連結:https://www.tuicool.com/articles/ua2qaey

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 容器環境的JVM記憶體設定最佳實踐