Redis 對象

    Redis並無直接使用SDS、雙端鏈表、字典、壓縮列表、整數集合等這些數據結構,而是基於這些數據結構建立了一個對象系統,這個對象系統包含字符串對象(String)、列表對象(List)、哈希對象(Hash)、集合對象(Set)和有序集合對象(SortedSet)這5種類型,每種對象都用到了至少一種數據結構。redis

    這種設計的好處是將咱們真實使用的對象提到了底層數據結構層級之上,讓咱們能夠對同一個對象類型有不一樣的實現,而後根據不一樣的場景使用不一樣的數據結構實現,優化使用效率。算法

    除此以外,Redis對象系統還實現了基於引用計數範式的內存回收機制(即每一個對象還有一個引用計數字段,每當有其餘對象引用便計數加1,當引用計數爲0時,表示沒有其餘對象引用這個對象,便可以回收,這種訪問有個明顯缺點,即存在相互引用狀況致使對象不能被回收),回收再也不使用對象的內存;另外,Redis還經過引用計數方式實現了對象共享機制,這一機制能夠在適當條件下,經過讓多個數據庫鍵共享同一個對象節約內存。數據庫

    最後,Redis對象帶有訪問時間記錄信息,能夠用於計算數據庫鍵的空轉時長,在服務器啓用了maxmemory功能而且回收內存的算法爲volatile-lru或者allkeys-lru的狀況下,當內存超過maxmemory設置的閥值,空轉時長較大的那些鍵可能會優先被服務器回收。具體能夠查看配置文件maxmemory和maxmemory-policy選項的說明介紹。緩存

redis.h/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;
  • type:表示當前redis對象的類型,能夠使用type命令查看某個key對應的值的redisObject的類型,包含5種可能值:
    • REDIS_STRING 字符串對象,type命令輸出爲"string"
    • REDIS_LIST 列表對象,type命令輸出爲"list"
    • REDIS_HASH 哈希對象,type命令輸出爲"hash"
    • REDIS_SET 集合對象,type命令輸出爲"set"
    • REDIS_ZSET 有序集合對象,type命令輸出爲"zset"
  • encoding:表示當前對象使用的數據結構類型,即ptr指針指向的對象對應的數據結構,能夠使用obejct encoding <key>命令查出對應值的數據結構,可能值以下:
    • REDIS_ENCODING_INT long類型的整數,object encoding命令返回值「int」
    • REDIS_ENCODING_EMBSTR embstr編碼的簡單動態字符串,object encoding命令返回值「embstr」
    • REDIS_ENCODING_RAW 簡單動態字符串,object encoding命令返回值「raw」
    • REDIS_ENCODING_HT 字典,object encoding命令返回值「hashtable」
    • REDIS_ENCODING_LINKEDLIST 雙端鏈表,object encoding命令返回值「linkedlist」
    • REDIS_ENCODING_ZIPLIST 壓縮鏈表,object encoding命令返回值「ziplist」
    • REDIS_ENCODING_INTSET 整數集合,object encoding命令返回值「intset」
    • REDIS_ENCODING_SKIPLIST 跳躍表,object encoding命令返回值「skiplist」

編碼與類型對應關係以下:服務器

  • refcount:引用計數,當計數爲0時,用於內存回收
  • ptr:指向對象實際結構的指針

 

字符串對象

字符串對象編碼能夠是int、raw或者embstr,字符串對象是惟一會潛逃到其餘數據結構中使用的結構,編碼切換條件以下:數據結構

  • 若是保存的是一個字符串值(不能轉化成整數),且長度大於32字節,使用raw即SDS
  • 若是保存的是一個字符串值(不能轉化成整數),且長度小於等於32字節,使用embstr
  • 若是保存的是一個整數,使用int即c語言的long類型

embstr與raw(SDS)結構徹底同樣,惟一的卻別是若是使用raw(SDS)會進行兩次內存分配,先分配建立SDS,而後分配建立redisObject,再使ptr指針指向SDS,即SDS和redisObject的內存地址能夠不連續。而embstr的SDS結構和redisObject結構內存連續,只須要一次內存分配,embstr優點:優化

  • 只須要一次內存分配
  • 只須要一次內存回收
  • 因爲內存連續,更利於CPU高速緩存載入

字符串各個數據結構之間調用不一樣方法的轉換:編碼

列表對象

列表對象的編碼能夠是ziplist或者linkedlist,轉換規則,當知足下面任意一條將使用linkedlist:spa

  • 列表對象所存字符串元素長度有一個及以上大於等於64字節;
  • 列表對象所存元素數量大於等於512;

上面兩個條件的閥值能夠經過配置文件list-max-ziplist-value和list-max-ziplist-entries兩個屬性進行修改。設計

ziplist和linkedlist方法實現差別:

哈希對象

哈希對象的編碼能夠是ziplist或者hashtable,ziplist在存鍵值對時老是兩個相關聯的鍵值對緊挨在一塊兒,建在前值在後,每次都在表尾添加,即越後添加的鍵值對越靠近表尾。

編碼切換條件,知足任意一個,將使用hashtable:

  • 任意一個或以上鍵或者值長度大於等於64字節
  • 鍵值對數量大於等於512

上限閥值能夠經過配置中的hash-max-ziplist-value和hash-max-ziplist-entries屬性修改。

不一樣數據結構方法實現差別:

集合對象

集合對象的編碼能夠是intset或者hashtable,切換條件以下,知足任一會使用hashtable:

  • 集合全部元素有一個或以上不是整數值
  • 集合元素數量大於等於512,能夠經過配置set-max-intset-entries設置

方法實現差別:

有序集合對象

有序集合的編碼能夠是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編碼:

  • 保存的元素數量大於等於128
  • 保存的元素長度由一個或以上長度大於等於64字節

能夠用配置中zset-max-ziplist-entries和zset-max-ziplist-value屬性修改

方法實現區別:

相關文章
相關標籤/搜索