圖解Redis之數據結構篇——壓縮列表

前言

    同整數集合一樣壓縮列表也不是基礎數據結構,而是 Redis 自己設計的一種數據存儲結構。它有點兒類似數組,通過一片連續的內存空間,來存儲數據。不過,它跟數組不同的一點是,它允許存儲的數據大小不同。

一、壓縮列表

    聽到“壓縮”兩個字,直觀的反應就是節省內存。之所以說這種存儲結構節省內存,是相較於數組的存儲思路而言的。我們知道,數組要求每個元素的大小相同,如果我們要存儲不同長度的字符串,那我們就需要用最大長度的字符串大小作爲元素的大小(假設是20個字節)。存儲小於 20 個字節長度的字符串的時候,便會浪費部分存儲空間。

    數組的優勢佔用一片連續的空間可以很好的利用CPU緩存訪問數據。如果我們想要保留這種優勢,又想節省存儲空間我們可以對數組進行壓縮。

     但是這樣有一個問題,我們在遍歷它的時候由於不知道每個元素的大小是多少,因此也就無法計算出下一個節點的具體位置。這個時候我們可以給每個節點增加一個lenght的屬性。

    如此。我們在遍歷節點的之後就知道每個節點的長度(佔用內存的大小),就可以很容易計算出下一個節點再內存中的位置。這種結構就像一個簡單的壓縮列表了。

二、Redis壓縮列表

    壓縮列表(zip1ist)是列表和哈希的底層實現之一。

    當一個列表只包含少量列表項,並且每個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼Redis就會使用壓縮列表來做列表的底層實現。

    當一個哈希只包含少量鍵值對,比且每個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字符串,那麼Redis就會使用壓縮列表來做哈希的底層實現。

2.1 Redis壓縮列表的構成

    壓縮列表是Redis爲了節約內存而開發的,是由一系列特殊編碼的連續內存塊組成的順序型(sequential)數據結枃。一個壓縮列表可以包含任意多個節點(entry),每個節點可以保存一個字節數組或者一個整數值,如下圖。

示例:

    如上圖,展示了一個總長爲80字節,包含3個節點的壓縮列表。如果我們有一個指向壓縮列表起始地址的指針p,那麼表爲節點的地址就是P+60。

2.2 Redis壓縮列表節點的構成

    每個壓縮列表節點可以保存一個字節數組或者一個整數值。其中,字節數組可以是以下三種長度中的一種。

  • 長度小於等於63(2^6-1)字節的字節數組;
  • 長度小於等於16383(2^14-1)字節的字節數組
  • 長度小於等於4294967295(2^32-1)字節的字節數組

整數值可以是以下6種長度中的一種

  • 4位長,介於0至12之間的無符號整數
  • 1字節長的有符號整數
  • 3字節長的有符號整數
  • int16_t類型整數
  • int32_t類型整數
  • int64_t類型整數

    節點的 previous_entry_length屬性以字節爲單位,記錄了壓縮列表中前一個節點的長度。 previous_entry_length屬性的長度可以是1字節或者5字節。

  • 如果前一節點的長度小於254字節,那麼 previous_entry_length屬性的長度爲1字節,前一節點的長度就保存在這一個字節裏面。
  • 如果前一節點的長度大於等於254字節,那麼 previous_entry_length屬性的長度爲5字節:其中屬性的第一字節會被設置爲0xFE(十進制值254),而之後的四個字節則用於保存前一節點的長度.

    節點的encoding屬性記錄了節點的content屬性所保存數據的類型以及長度。

  • 一字節、兩字節或者五字節長,值的最高位爲00、01或者10的是字節數組編碼這種編碼表示節點的 content屬性保存着字節數組,數組的長度由編碼除去最高兩位之後的其他位記錄。
  • 一字節長,值的最高位以11開頭的是整數編碼:這種編碼表示節點的content屬性保存着整數值,整數值的類型和長度由編碼除去最高兩位之後的其他位記錄。

    節點的content屬性負責保存節點的值,節點值可以是一個字節數組或者整數,值的類型和長度由節點的encoding屬性決定。

  • 編碼的最高兩位00表示節點保存的是一個字節數組。
  • 編碼的後六位001011記錄了字節數組的長度11。
  • content屬性保存着節點的值"hello world"。
  • 編碼11000000表示節點保存的是一個int16_t類型的整數值;
  • content屬性保存着節點的值10086

2.3 常用操作的時間複雜度

操作 時間複雜度
創建一個新的壓縮列表 O(1)
創建一個包含給定值的新節點,並將這個新節點添加到壓縮列表的表頭或者表尾 平均O(N),最壞O(N^2)(可能發生連鎖更新)
將包含給定值的新節點插人到給定節點之後 平均O(N),最壞O(N^2)(可能發生連鎖更新)
返回壓縮列表給定索引上的節點 O(N)
在壓縮列表中査找並返回包含了給定值的節點 因爲節點的值可能是一個字節數組,所以檢查節點值和給定值是否相同的複雜度爲O(N),而查找整個列表的複雜度則爲(N^2)
返回給定節點的下一個節點 O(1)
返回給定節點的前一個節點 O(1)
獲取給定節點所保存的值 O(1)
從壓縮列表中刪除給定的節點 平均O(N),最壞O(N^2)(可能發生連鎖更新)
刪除壓縮列表在給定索引上的連續多個 平均O(N),最壞O(N^2)(可能發生連鎖更新)
返回壓縮列表目前佔用的內存字節數 O(1)
返回壓縮列表目前包含的節點數量 點數量小於65535時爲O(1),大於65535時爲O(N)

本文重點

  • 壓縮列表是Redis爲節約內存自己設計的一種順序型數據結構。

  • 壓縮列表被用作列表鍵和哈希鍵的底層實現之一。
  • 壓縮列表可以包含多個節點,每個節點可以保存一個字節數組或者整數值。
  • 添加新節點到壓縮列表,或者從壓縮列表中刪除節點,可能會引發連鎖更新操作,但這種操作出現的機率並不高。

參考

《Redis設計與實現》

《Redis開發與運維》

《Redis官方文檔》

-----END-----

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章