【redis】-- 資料結構及底層編碼篇 雙木l之林 發表於2021-01-05
資料結構
String
Hash
List
使用場景
關注列表
粉絲列表
訊息列表
場景使用口訣:
lpush+lpop=Stack(棧)
lpush+rpop=Queue(佇列)
lpsh+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(訊息佇列)
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
快速列表 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寫入資料時自動完成, 這個轉換過程是不可逆的, 轉換規則只能從小記憶體編碼向大記憶體編碼轉換