Redis並無直接使用SDS、雙端鏈表、字典、壓縮列表、整數集合等這些數據結構,而是基於這些數據結構建立了一個對象系統,這個對象系統包含字符串對象(String)、列表對象(List)、哈希對象(Hash)、集合對象(Set)和有序集合對象(SortedSet)這5種類型,每種對象都用到了至少一種數據結構。redis
這種設計的好處是將咱們真實使用的對象提到了底層數據結構層級之上,讓咱們能夠對同一個對象類型有不一樣的實現,而後根據不一樣的場景使用不一樣的數據結構實現,優化使用效率。算法
除此以外,Redis對象系統還實現了基於引用計數範式的內存回收機制(即每一個對象還有一個引用計數字段,每當有其餘對象引用便計數加1,當引用計數爲0時,表示沒有其餘對象引用這個對象,便可以回收,這種訪問有個明顯缺點,即存在相互引用狀況致使對象不能被回收),回收再也不使用對象的內存;另外,Redis還經過引用計數方式實現了對象共享機制,這一機制能夠在適當條件下,經過讓多個數據庫鍵共享同一個對象節約內存。數據庫
最後,Redis對象帶有訪問時間記錄信息,能夠用於計算數據庫鍵的空轉時長,在服務器啓用了maxmemory功能而且回收內存的算法爲volatile-lru或者allkeys-lru的狀況下,當內存超過maxmemory設置的閥值,空轉時長較大的那些鍵可能會優先被服務器回收。具體能夠查看配置文件maxmemory和maxmemory-policy選項的說明介紹。緩存
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;
編碼與類型對應關係以下:服務器
字符串對象編碼能夠是int、raw或者embstr,字符串對象是惟一會潛逃到其餘數據結構中使用的結構,編碼切換條件以下:數據結構
embstr與raw(SDS)結構徹底同樣,惟一的卻別是若是使用raw(SDS)會進行兩次內存分配,先分配建立SDS,而後分配建立redisObject,再使ptr指針指向SDS,即SDS和redisObject的內存地址能夠不連續。而embstr的SDS結構和redisObject結構內存連續,只須要一次內存分配,embstr優點:優化
字符串各個數據結構之間調用不一樣方法的轉換:編碼
列表對象的編碼能夠是ziplist或者linkedlist,轉換規則,當知足下面任意一條將使用linkedlist:spa
上面兩個條件的閥值能夠經過配置文件list-max-ziplist-value和list-max-ziplist-entries兩個屬性進行修改。設計
ziplist和linkedlist方法實現差別:
哈希對象的編碼能夠是ziplist或者hashtable,ziplist在存鍵值對時老是兩個相關聯的鍵值對緊挨在一塊兒,建在前值在後,每次都在表尾添加,即越後添加的鍵值對越靠近表尾。
編碼切換條件,知足任意一個,將使用hashtable:
上限閥值能夠經過配置中的hash-max-ziplist-value和hash-max-ziplist-entries屬性修改。
不一樣數據結構方法實現差別:
集合對象的編碼能夠是intset或者hashtable,切換條件以下,知足任一會使用hashtable:
方法實現差別:
有序集合的編碼能夠是ziplist或者skiplist。ziplist編碼也和集合對象使用同樣,只是插入時會確保score的升序排列,有序列表的skiplist數據結構有些特殊,源代碼以下:
/* * 有序集合 */ typedef struct zset { // 字典,鍵爲成員,值爲分值 // 用於支持 O(1) 複雜度的按成員取分值操做 dict *dict; // 跳躍表,按分值排序成員 // 用於支持平均複雜度爲 O(log N) 的按分值定位成員操做 // 以及範圍操做 zskiplist *zsl; } zset;
同時使用zskiplist和dict,這樣作是爲了保證能夠同時使用skiplist進行高效查找遍歷,使用dict高效經過key獲取score,因爲使用了共享對象,因此內存並無浪費。
編碼轉換條件,知足任一對象使用ziplist編碼:
能夠用配置中zset-max-ziplist-entries和zset-max-ziplist-value屬性修改
方法實現區別: