Redis之數據結構

本文是《Redis設計與實現》的閱讀筆記。數組

基本數據結構

簡單動態字符串

Redis中的字符串使用「簡單動態字符串」(SDS)表示,不管是字符串值仍是鍵底層都採用「簡單動態字符串」。數據結構

  • free:未使用空間大小;
  • len:字符串長度;
  • buf:以空字符結尾的char數組。

爲了減小內存從新分配次數,SDS作出瞭如下優化:函數

  • 空間預分配:額外分配的未使用空間數量由如下公式決定:
    • 若是對SDS進行修改以後,SDS的長小於1MB,那麼程序分配和len 屬性一樣大小的未使用空間,
    • 若是對SDS進行修改以後,SDS的長度將大於等千1MB, 那麼程序會分配 1MB 的未使用空間。
  • 惰性空間釋放:程序並不當即使用內存重分配來回收縮短後多出來的字節,而是使用free屬性將這些字節的數量記錄起來,並等待未來使用。

鏈表

鏈表是Redis列表鍵實現之一,也是不少其餘功能實現的基礎,鏈表節點定義以下:優化

鏈表的完整結構體定義以下編碼

  • head爲表頭指針;
  • tail爲表尾指針;
  • len爲鏈表長度計數器;
  • dup爲函數指針,用於複製鏈表節點所保存的值;
  • free爲函數指針,用於釋放鏈表節點所保存的值;
  • match爲函數指針,則用於對比鏈表節點所保存的值和另外一個輸入值是否相等。

字典

字典將鍵和值進行關聯,當哈希鍵中的鍵值對數量比較多,或者鍵值對中的元素比較大的時候,採用字典做爲底層實現。字節的數據結構以下設計

哈希表結構dict中,table屬性是一個數組,每一個元素都是指向dictEntry結構的指針,size屬性記錄了哈希表的大小,sizemask屬性的值老是等於size-1,而used屬性則記錄了哈希表目前已有節點(鍵值對)的數量。3d

字典結構dictType中有兩個哈希表ht[0]ht[1]ht[l]哈希表只會在對 ht[0]哈希表進行rehash時使用,rehashidx它記錄了rehash目前的進度。type屬性是一個指向dictType結構的指針,dictType結構保存了一簇用於操做特定類型鍵值對的函數,例如計算哈希值、複製鍵、複製值、對比鍵、銷燬鍵和銷燬值的函數。而privdata屬性則保存了須要傳給那些類型特定函數的可選參數。指針

爲了讓哈希表的負載因子維持在一個合理的範圍以內,當哈希表保存的鍵值對數量太多或者太少時,程序須要對哈希表的大小進行相應的擴展或者收縮。code

  • 若是執行的是擴展操做,那麼ht[l]的大小爲第一個大於等於ht[0].used*22^m
  • 若是執行的是收縮操做,那麼ht[1]的大小爲第一個大於等於ht[O].used2^m

字典採用漸進式rehash,好處在千它採起分而治之的方式,將 rehash鍵值對所需的計算工做均攤到對字典的每一個添加、刪除、查找和更新操做上。cdn

跳躍表

跳躍表能夠用於有序集合鍵的底層實現,數據結構以下

zskiplist結構包含如下屬性:

  • header: 指向跳躍表的表頭節點。
  • tail: 指向跳躍表的表尾節點。
  • level: 記錄目前跳躍表內,層數最大的那個節點的層數。
  • length: 記錄跳躍表的長度。

zskiplistNode 結構,該結構包含如下屬性:

  • 層 (level) : 每一個層都帶有兩個屬性:前進指針和跨度。前進指針用於 訪問位於表尾方向的其餘節點,而跨度則記錄了前進指針所指向節點和當前節點的 距離。
  • 後退 (backward) 指針:指向位於當前節點的前一個節點。
  • 分值 (score): 節點按各自所保存的分值從小到大排列。
  • 成員對象 (obj): 節點所保存的成員對象。

整數集合

當一個集合只包含整數值元素,而且這個集合的元素數董很少時, Redis 就會使用整數集合做爲集合鍵的底層實現。

contents數組是整數集合的底層數據存放位置,各個項在數組中按值的大小從小到大有序地排列,而且數組中不包含任何重複項。length屬性記錄了整數集合包含的元素數量,encoding屬性決定了整數類型(INTSET_ENC_INT16/INTSET_ENC_INT32/INTSET_ENC_INT64)。新元素的類型比整數集合現有全部元素的類型都要長時,整數集合須要先進行升級。

壓縮鏈表

若是列表鍵或者哈希鍵包含的元素比較少,那麼會採用壓縮列表做爲底層實現。

屬性 說明
zlbytes 記錄整個壓縮列表佔用的內存字節數
zltail 記錄壓縮列表表尾節點距離壓縮列表的起始地址有多少字節
zllen 記錄了壓縮列表包含的節點數量
entryX 壓縮列表包含的各個節點
zlend 特殊值 OxFF (十進制 255), 用於標記壓縮列表的末端

entryX的數據結構以下

節點的previous_entry_length記錄了壓縮列表中前一個節 點的長度,節點的encoding屬性記錄了節點的content屬性所保存數據的類型以及長度,節點的content屬性負責保存節點的值。

數據結構和對象

Redis對象的結構體定義以下

type爲對象類型,encoding爲底層使用的數據結構,而ptr是指向底層數據結構的指針。Redis一共支持了五種數據類型,能夠使用TYPE命令查看對象的類型。

對象 對象type屬性的值 TYPE命令的輸出
字符串對象 REDIS_STRING "string"
列表對象 REDIS_LIST "list"
哈希對象 REDIS_HASH "hash"
集合對象 REDIS_SET "set"
有序集合對象 REDIS_ZSET "zset"

而對象具體使用的數據結構能夠用OBJECT ENCODING命令獲取。

類型 編碼 對象
REDIS_STRING REDIS_ENCODING_INT 使用整數值實現的字符串對象
REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 編碼的簡單動態字符串實現的字符串對象
REDIS_STRING REDIS_ENCODING_RAW 使用簡單動態字符串實現的字符串對象
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用壓縮列表實現的列表對象
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用雙端鏈表實現的列表對象
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用壓縮列表實現的哈希對象
REDIS_HASH REOIS_ENCODING_HT 使用字典實現的哈希對象
REDIS_SET REDIS_ENCODING_INTSET 使用整數集合實現的集合對象
REDIS_SET REDIS_ENCODING_HT 使用字典實現的集合對象
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用壓縮列表實現的有序集合對象
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳躍表和字典實現的有序集合對象

不一樣類型的對象的編碼選擇規則以下:

字符串對象

  • 若是一個字符串對象保存的是整數值,而且這個整數值能夠用 long 類型來表示,那麼 字符串對象會將整數值保存在字符串對象結構的 ptr 屬性裏面
  • 若是字符串對象保存的是一個字符串值,而且這個字符串值的長度大於 32 字節,那麼 字符串對象將使用一個簡單動態字符串 (SDS) 來保存這個字符串值
  • 若是字符串對象保存的是一個字符串值,而且這個字符串值的長度小千等於 32 字節, 那麼字符串對象將使用 embstr 編碼的方式來保存這個字符串值。

列表對象

當列表對象能夠同時知足如下兩個條件時,列表對象使用ziplist編碼:

  • 列表對象保存的全部字符串元素的長度都小千 64 字節;
  • 列表對象保存的元素數量小千 512 個;

不能知足這兩個條件的列表對象須要使用 linkedlist 編碼。

恰希對象

當哈希對象能夠同時知足如下兩個條件時,哈希對象使用 ziplist 編碼:

  • 哈希對象保存的全部鍵值對的鍵和值的字符串長度都小千 64 字節;
  • 哈希對象保存的鍵值對數量小千 512 個;

不能知足這兩個條件的哈希對象須要使用 hash able 編碼。

集合對象

當集合對象能夠同時知足如下兩個條件時,對象使用 intset 編碼: 集合對象保存的全部元素都是整數值;

  • 集合對象保存的元素數量不超過 512 個。

不能知足這兩個條件的集合對象須要使用 hash table 編碼。

有序集合對象

當有序集合對象能夠同時知足如下兩個條件時,對象使用ziplist編碼:

  • 有序集合保存的元素數量小於 128 個;
  • 有序集合保存的全部元素成員的長度都小於 64 字節;

不能知足以上兩個條件的有序集合對象將使用skiplist編碼。

有序集合對象在維護skiplist的同時,使用了dict,使得可以快速完成成員查詢。

相關文章
相關標籤/搜索