前面說到,Redis對象有5種類型;無論是哪種類型,Redis都不會直接存儲,
而是通過redisObject對象進行存儲。
- redisObject對象非常重要,Redis對象的類型、內部編碼、內存回收、共享對象等功能,都需要redisObject支持,下面將通過redisObject的結構來說明它是如何起作用的。
- redisObject的定義如下(列出了與保存數據有關的三個屬性):
typedef struct redisObject
{
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS;
/* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;
redisObject的每個字段的含義和作用如下:
(1)type
type字段表示對象的類型,佔4個比特;目前包括REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。
當我們執行type命令時,便是通過讀取RedisObject的type字段獲得對象的類型;如下圖所示:
(2)encoding
- encoding表示對象的內部編碼,佔4個比特。
對於redis支持的每種類型都至少有兩種編碼,對於字符串有int、embsre、row三種
通過encoding屬性,redis可以根據不同的使用場景來對對象使用不同的編碼,大大提高的redis的靈活性和效率。
- 以列表對象爲例,有壓縮列表和雙端鏈表兩種編碼方式;如果列表中的元素較少,
Redis傾向於使用壓縮列表進行存儲,因爲壓縮列表佔用內存更少,而且比雙端鏈表可以更快載入;當列表對象元素較多時,壓縮列表就會轉化爲更適合存儲大量元素的雙端鏈表。3.2版本以後都採用quicklist, 是壓縮鏈表和雙端鏈表的結合。 - 什麼意思呢,比如,一個包含三個結點的quicklist,如果每個結點的ziplist又包含四個數據項,那麼對外表現上,這個list就總共包含12個數據項。這樣的設計,實際上是對於時間和空間的一種折中。
- linkedlist便於在表的兩端進行push和pop操作,但是它的內存開銷較大。首先,它的每個節點除了要保存數據之外還要額外保存兩個指針;其次,雙向鏈表的各個節點是單獨的內存塊,地址不連續,容易產生內存碎片,還容易造成抖動。
ziplist由於是一整塊連續的內存,存儲效率很高,但不利於添加和刪除操作,每次都會重新realloc,尤其是當ziplist很長的時候,一次realloc造成的開銷特別的大,查詢的開銷也特別的大。
列表、hash、集合、有序結合的內部編碼實例圖: