深入瞭解Redis(3)-物件
Redis主要的資料結構有簡單動態字串(SDS)、雙端連結串列、字典、壓縮列表、整數集合,等等。但Redis並沒有直接使用這些資料結構來實現鍵值對資料庫, 而是基於這些資料結構建立了一個物件系統, 這個系統包含字串物件、列表物件、雜湊物件、集合物件和有序集合物件這五種型別的物件, 每種物件都用到了至少一種我們前面所介紹的資料結構。
通過這五種不同型別的物件, Redis 可以在執行命令之前, 根據物件的型別來判斷一個物件是否可以執行給定的命令。 使用物件的另一個好處是, 我們可以針對不同的使用場景, 為物件設定多種不同的資料結構實現, 從而優化物件在不同場景下的使用效率。
除此之外, Redis 的物件系統還實現了基於引用計數技術的記憶體回收機制(下一篇會詳細講到): 當程式不再使用某個物件的時候, 這個物件所佔用的記憶體就會被自動釋放; 另外, Redis 還通過引用計數技術實現了物件共享機制, 這一機制可以在適當的條件下, 通過讓多個數據庫鍵共享同一個物件來節約記憶體。
物件的型別與編碼
Redis 使用物件來表示資料庫中的鍵和值, 每次當我們在 Redis 的資料庫中新建立一個鍵值對時, 我們至少會建立兩個物件, 一個物件用作鍵值對的鍵(鍵物件), 另一個物件用作鍵值對的值(值物件)。
舉個例子, SET命令在資料庫中建立了一個新的鍵值對, 其中鍵值對的鍵是一個包含了字串值"msg"
的物件, 而鍵值對的值則是一個包含了字串值"helloworld"
的物件。
Redis 中的每個物件都由一個redisObject
結構表示, 該結構中和儲存資料有關的三個屬性分別是type
屬性、encoding
屬性和ptr
屬性:
typedef struct redisObject {// 型別 unsigned type:4; // 編碼 unsigned encoding:4; // 指向底層實現資料結構的指標 void *ptr; // ... } robj;
型別
物件的type
屬性記錄了物件的型別, 這個屬性的值可以是表 8-1 列出的常量的其中一個。
表 8-1 物件的型別
型別常量 | 物件的名稱 |
---|---|
REDIS_STRING |
字串物件 |
REDIS_LIST |
列表物件 |
REDIS_HASH |
雜湊物件 |
REDIS_SET |
集合物件 |
REDIS_ZSET |
有序集合物件 |
對於 Redis 資料庫儲存的鍵值對來說, 鍵總是一個字串物件, 而值則可以是字串物件、列表物件、雜湊物件、集合物件或者有序集合物件的其中一種, 因此:
- 當我們稱呼一個數據庫鍵為“字串鍵”時, 我們指的是“這個資料庫鍵所對應的值為字串物件”;
- 當我們稱呼一個鍵為“列表鍵”時, 我們指的是“這個資料庫鍵所對應的值為列表物件”。
編碼和底層實現
物件的ptr
指標指向物件的底層實現資料結構, 而這些資料結構由物件的encoding
屬性決定。
encoding
屬性記錄了物件所使用的編碼, 也即是說這個物件使用了什麼資料結構作為物件的底層實現, 這個屬性的值可以是表 8-3 列出的常量的其中一個。
表 8-3 物件的編碼
編碼常量 | 編碼所對應的底層資料結構 |
---|---|
REDIS_ENCODING_INT |
long 型別的整數 |
REDIS_ENCODING_EMBSTR |
embstr 編碼的簡單動態字串 |
REDIS_ENCODING_RAW |
簡單動態字串 |
REDIS_ENCODING_HT |
字典 |
REDIS_ENCODING_LINKEDLIST |
雙端連結串列 |
REDIS_ENCODING_ZIPLIST |
壓縮列表 |
REDIS_ENCODING_INTSET |
整數集合 |
REDIS_ENCODING_SKIPLIST |
跳躍表和字典 |
通過encoding
屬性來設定物件所使用的編碼, 而不是為特定型別的物件關聯一種固定的編碼, 極大地提升了 Redis 的靈活性和效率, 因為 Redis 可以根據不同的使用場景來為一個物件設定不同的編碼, 從而優化物件在某一場景下的效率。
舉個例子, 在列表物件包含的元素比較少時, Redis 使用壓縮列表作為列表物件的底層實現:
- 因為壓縮列表比雙端連結串列更節約記憶體, 並且在元素數量較少時, 在記憶體中以連續塊方式儲存的壓縮列表比起雙端連結串列可以更快被載入到快取中;
- 隨著列表物件包含的元素越來越多, 使用壓縮列表來儲存元素的優勢逐漸消失時, 物件就會將底層實現從壓縮列表轉向功能更強、也更適合儲存大量元素的雙端連結串列上面;