【redis】-- 資料結構及底層編碼篇

雙木l之林發表於2021-01-05

資料結構

  • String
    • bitmaps也是“字串”
    • 使用場景
      • 快取
      • 計數
      • 共享Session
      • 限速(限流)
  • Hash
    • 使用場景
      • 快取使用者資訊(多屬性)
  • List
    • 使用場景
      • 關注列表
      • 粉絲列表
      • 訊息列表
      • 場景使用口訣:
        • lpush+lpop=Stack(棧)
        • lpush+rpop=Queue(佇列)
        • lpsh+ltrim=Capped Collection(有限集合)
        • lpush+brpop=Message Queue(訊息佇列)
  • Set
    • 使用場景
      • 標籤(關係的維護必須是原子性,lua)
        • 標籤新增使用者
        • 使用者調加標籤
    • set 是可以自動排重的
  • Sorted Set(ZSet)
    • zset增加了一個權重引數score,使得集合中的元素能夠按score進行有序排列
    • 使用場景
      • 排行榜
      • 用在"延時佇列",通過將對應的過期時間設定為對應的score實現

底層編碼

  • 字串 SDS(Simple Dynamic String):
    • emb:
      • 長度 <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT
      • 它將RedisObject物件頭和SDS物件連續存在一起,使用malloc方法一次分配,高併發寫入場景中建議字串小於該值,減少建立redisObject記憶體分配次數, 從而提高效能
    • raw:
      • 長度 > OBJ_ENCODING_EMBSTR_SIZE_LIMIT
      • raw 儲存形式不一樣,它需要兩次malloc,兩個物件頭在記憶體地址上一般是不連續的
  • 長度OBJ_ENCODING_EMBSTR_SIZE_LIMIT與redis版本有關
  • 結構圖

  • 字典 dict
    • dict 結構內部包含兩個 hashtable,通常情況下只有一個 hashtable 是有值的。但是在dict擴容縮容時,需要分配新的hashtable,然後進行漸進式搬遷(),這時候兩個hashtable儲存的分別是舊的hashtable和新的hashtable。待搬遷結束後,舊的hashtable被刪除,新的hashtable 取而代之
    • 漸進式 rehash:漸進式 rehash 小步搬遷
      • 被動搬遷:來自客戶端指令(hset/hdel)
      • 主動搬遷:定時任務databaseCron
    • 擴容條件
      • 正常情況下,當hash表中元素的個數等於第一維陣列的長度時,就會開始擴容,擴容的新陣列是原陣列大小的2倍。不過如果Redis正在做bgsave,為了減少記憶體頁的過多分離 (Copy On Write),Redis儘量不去擴容(dict_can_resize),但是如果hash表已經非常滿了,元素的個數已經達到了第一維陣列長度的5倍(dict_force_resize_ratio),說明hash表已經過於擁擠了,這個時候就會強制擴容
    • 縮容條件
      • 縮容的條件是元素個數低於陣列長度的10%。縮容不會考慮 Redis 是否正在做 bgsave

  • 壓縮列表 ziplist
    • 支援雙向遍歷
    • 級聯更新 (O(n)~O(n2))

  • 快速列表 quicklist (待補充)
    • quicklist 是 ziplist和linkedlist的混合體,它將linkedlist按段切分,每一段使用 ziplist 來緊湊儲存,多個 ziplist 之間使用雙向指標串接起來
    • 為了進一步節約空間,Redis還會對ziplist進行壓縮儲存,使用LZF演算法壓縮,可以選擇壓縮深度
    • quicklist 內部預設單個ziplist長度為8k位元組,超出了這個位元組數,就會新起一個ziplist。 ziplist 的長度由配置引數 list-max-ziplist-size 決定

  • 跳躍列表 skiplist
    • 基礎結構:Redis 的跳躍表共有64層,意味著最多可以容納2^64次方個元素。每一個 kv 塊對應的結構如下面的程式碼中的 zslnode 結構,kv header也是這個結構,只不過 value 欄位是 null 值——無效的, score 是Double.MIN_VALUE,用來墊底的。 kv 之間使用指標串起來形成了雙向連結串列結構,它們是有序 排列的,從小到大。不同的 kv 層高可能不一樣,層數越高的kv越少。同一層的kv會使用指標串起來。每一個層元素的遍歷都是從 kv header 出發

  • 緊湊列表 listpack
    • 對 ziplist 結構的改進,在儲存空間上會更加節省,而且結構上也比 ziplist 要精簡
    • 元素之間獨立,不存在級聯更新


  • 注意:編碼型別轉換在Redis寫入資料時自動完成, 這個轉換過程是不可逆的, 轉換規則只能從小記憶體編碼向大記憶體編碼轉換

  • 附圖

相關文章