對象
前邊學習了Redis底層實現的各種數據結構, 包括SDS
, list
, skiplist
, dict
, intset
, ziplist
等, 但redis并未直接使用這些數據結構來構建數據庫、而是基于這些數據結構構建了一個對象系統
, 這個系統包括 字符串對象
, 列表對象
, hash對象
, 集合對象
, 有序集合對象
這五種類型的對象.
通過這5種不同的對象、Redis可以在執行命令前, 根據對象的類型來判斷一個對象是否可以執行給定的命令, 使用對象的另一個好處是, 可以針對不同的使用場景、為對象設置不同的數據結構的實現、優化對象在不同使用場景下的使用效率.
此外、Redis的對象系統還實現了基于引用計數技術的內存回收機制、當程序不再使用某個對象時、對象占用的內存會被自動釋放, 且: Redis 還通過引用計數技術實現了對象共享機制, 可以在適當調節下、讓多個數據庫鍵共享同一個對象來節約內存
Redis的對象帶有訪問時間記錄信息, 該信息可以用于計算數據庫鍵的空轉時長, 在服務器啟用了 maxmemory
功能的情況下、空轉時長較大的鍵會被優先刪除
對象的類型和編碼
Redis使用對象表示數據庫中的鍵和值, 當在redis數據庫中創建一個鍵值對時、至少會創建兩個對象: 鍵對象 和 值對象. eg. Set 命令在數據庫中創建一個鍵值對
redis> set msg "hello world"
OK
就包含了一個鍵對象msg
的字符串對象 和 一個值對象 hello world
Redis中每個對象由一個redisObject
結構表示, 和保存數據有關的3個屬性分表是 type屬性
、encoding屬性
和 ptr
屬性
typedef struct redisObject {
unsigned type:4; // 類型
unsigned encoding:4; // 編碼
void *ptr; // 指向底層實現數據結構的指針
//...
}
類型
對象的type屬性
記錄了對象的類型, 屬性值可以是下邊任何一個
類型常量 | 對象名稱 | type命令輸出 |
---|---|---|
REDIS_STRING | 字符串對象 | String |
REDIS_LIST | 列表對象 | List |
REDIS_HASH | hash對象 | Hash |
REDIS_SET | 集合對象 | Set |
REDIS_ZSET | 有序集合對象 | Zset |
對于Redis保存的鍵值對來說、鍵總是一個字符串對象, 而值則可以是上邊任意一種對象, 所以:
- 當我們稱呼一個鍵為
字符串鍵
時, 指的是對應的值是字符串對象
- 當我們稱呼一個鍵為
列表鍵
時, 指的是對應的值是列表對象
type命令
的實現方式與此類似, 執行type命令返回的是值對象的類型, 而不是鍵對象的類型
編碼和底層實現
對象的ptr指針
指向對象的底層實現數據結構, 而這些數據結構由對象的encoding
屬性決定,
encoding
屬性記錄了對象使用的編碼, 即 對象使用了什么數據結構作為對象的底層實現, 可以是下表的任意一個
編碼常量 | 編碼對應的底層數據結構 |
---|---|
REDIS_ENCODING_INT | long類型的整數 |
REDIS_ENCODING_EMBSTR | embstr編碼的簡單動態字符串 |
REDIS_ENCODING_RAW | 簡單動態字符串 |
REDIS_ENCODING_HT | 字典 |
REDIS_ENCODING_LIKEDLIST | 雙端鏈表 |
REDIS_ENCODING_ZIPLIST | 壓縮列表 |
REDIS_ENCODING_INTSET | 整數集合 |
REDIS_ENCODING_SKIPLIST | 跳表 |
Reids每種類型的對象都至少使用了2種不同的編碼
類型 | 編碼 | 對象 |
---|---|---|
redis_string | redis_encoding_int<br />redis_encoding_embstr redis_encoding_raw |
使用整數值實現的字符串對象<br />使用embstr編碼的簡單動態字符串 用簡單動態字符串實現的字符串對象 |
redis_list | redis_encoding_ziplist<br />redis_encoding_linkedlist | 使用壓縮列表實現的列表對象<br />使用雙端鏈表實現的列表對象 |
redis_hash | redis_encoding_ziplist<br />redis_encoind_ht | 使用壓縮列表實現的hash對象<br />使用字典實現的hash對象 |
redis_set | redis_encoding_intset<br />redis_encoding_ht | 使用整數集合實現的集合對象<br />使用字典實現的集合 |
redis_zset | Redis_encoding_ziplist redis_encoding_skiplist |
使用壓縮列表實現的有序集合對象 使用跳表實現的有序集合對象 |
使用object encoding
命令可以查看一個數據庫鍵的值對象編碼
redis> set msg "hello world"
OK
redis> object encoding msg
"embstr"
redis> set story "long long long ..."
OK
redis> object encoding story
"raw"
通過encoding屬性來設定對象使用的編碼、而不是為特定類型的對象關聯一種固定的編碼、極大的提高了redis的靈活性和效率, 允許redis在特定場景下未一個對象設置不同的編碼, 優化使用效率.
eg. 在列表包含對象較少時, redis使用壓縮列表作為底層實現, 因為:
- 壓縮列表比雙端鏈表節省內存, 且元素數量少時、在內存中連續存儲的壓縮列表比雙端鏈表可以更快的載入緩存
- 隨元素增多、使用壓縮列表保存元素的優勢消失時、對象會將底層實現從壓縮列表轉向功能更強、也適合保存大量元素的雙端鏈表上
其它類型的對象也會使用不同類型的編碼進行類似的優化