Redis的每個鍵值都是使用一個redisObject結構體保存的:redis
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;
type字段表示數據類型:數組
/* Object types */ #define REDIS_STRING 0 #define REDIS_LIST 1 #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4
encoding表示內部編碼方式:bash
#define REDIS_ENCODING_RAW 0 /* Raw representation */ #define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_HT 2 /* Encoded as hash table */ #define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define REDIS_ENCODING_INTSET 6 /* Encoded as intset */ #define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
數據結構與編碼方式對應:數據結構
redis使用sdshdr類型的變量來存儲字符串,redisObject的ptr字段指向sdshdr的地址,sdshdr的定義以下:性能
struct sdshdr { int len; //buf已佔用的空間長度 int free; //buf中剩餘的空間長度 char but[]; //數據 真實存儲c字符串 }
好比執行set key "hello"時,內存結構以下:優化
當存儲字符串時,實際佔用空間就如上圖所示,字符串值存儲在sdshdr結構中。ui
若是值的內容能夠用一個64位符號整數表示,redis將會把值轉換成long,內存結構以下:編碼
能夠看出若是值是long,那麼將不會放到sdshdr結構中。spa
另外,redis會事先創建10000個key,分別存儲0到9999這些數字,若是值是0-9999,那麼能夠直接使用這10000個數字而不需在建立redisObject了。3d
當配置了maxmemory設置了redis的最大空間大小時,redis不會使用共享對象,由於對於每個鍵值都要上會用一個redisObject來記錄LRU信息。
當鍵值內容不超過39個字節時,reids會採用EMBSTR編碼,這個編碼的好處是把sdshdr結構與redisObject分配到連續的內存空間:
這麼作的好處是無論分配存儲仍是釋放內存,所須要的操做都從2次減小到1次。
當對EMBSTR編碼的字符串執行修改操做時(如APPEND操做),Redis會將其轉換爲RAW編碼。
散列類型內部使用HT或者ZIPLIST編碼。
兩種數據結構的切換配置在:
hash-max-ziplist-entries 512 hash-max-ziplist-value 64
當散列類型字段個數少於hash-max-ziplist-entries個,或者每一個字段名和字段值的長度都小於hash-max-ziplist-value個字節,Redis會用ziplist編碼存儲數據,不然就使用ht存儲數據,轉換過程是透明的。
Redis的ZIPLIST,是一種緊湊的編碼格式,它犧牲了部分讀取性能換區極高的空間利用率,因此當entries在較少的個數而且value在較小的長度時使用。
具體實現不在此記錄了,能夠搜索相關文章。
列表類型採用LINKEDLIST和ZIPLIST兩種編碼。
LINKEDLIST採用雙向鏈表,鏈表中的元素都是redisObject存儲的,此處與字符串優化方式相同。
兩種數據結構的切換配置在:
list-max-ziplist-entries 512 list-max-ziplist-value 64
轉換方式和Hash同樣。
其實linkedlist已經趨向於淘汰了,新版本的redis提供了QUICKLIST編碼方式:
將長列表分紅若干個以鏈表形式組織的ziplist,達到減小空間佔用的同時還能夠提高ziplist編碼性能的效果,具體實現能夠在網上搜索,此處不記錄了。
內部實現方式是HT和INTSET。
當元素都是整型且元素的個數小於配置文件中set-max-intset-entries=512個時,Redis會使用INTSET,不然使用HT。
typedef struct intset { uint32_t encoding; // 編碼類型 int16_t、int32_t、int64_t uint32_t length; // 長度 最大長度:2^32 int8_t contents[]; // 柔性數組 } intset;
其中centents存儲的就是集合中的元素,根據encoding不一樣,每一個元素佔用的大小也不一樣。默認是INT16(2字節),當新增長的整數沒法用2字節存儲時,Redis會將該集合的encoding升級到INT32(4字節),以此類推到INT64(8字節)。INTSET以有序的方式存儲元素,因此可用二分法查找,但添加或刪除元素都須要調整元素的內存位置,因此元素太多時性能很差,故定義了entries的個數限制。
INTSET轉成HT後,不會在自動轉回INTSET。
內部實現方式是SKIPLIST和ZIPLIST。
兩種數據結構的切換配置在:
zset-max-ziplist-entries 128 zset-max-ziplist-value 64
當使用ZIPLIST時,
每一個集合元素使用兩個緊挨在一塊兒的壓縮列表節點來保存,第一個節點保存元素的成員,第二個節點保存元素的分值。而且壓縮列表內的集合元素按分值從小到大的順序進行排列,小的放置在靠近表頭的位置,大的放置在靠近表尾的位置。
當使用SKIPLIST時,有序集合對象使用 zet 結構做爲底層實現,一個 zset 結構同時包含一個字典和一個跳躍表。
關於SKIPLIST,能夠在網上搜索redis zset skiplist數據結構,在此處不作詳細記錄了。