吊打面試官——redis

石灰聰發表於2020-11-26
  1. SDS是什麼?
    Redis中字串的實現,Simple Dynamic String簡單動態字串。

  2. 為什麼 Redis要用SDS實現字串?
    因為C語言本身沒有字串型別,只能用字元陣列char[]實現。
    1、使用字元陣列必須先給目標變數分配足夠的空間,否則可能會溢位。
    2、如果要獲取字元長度,必須遍歷字元陣列,時間複雜度是O(n)
    3、C字串長度的變更會對字元陣列做記憶體重分配。
    4、通過從字串開始到結尾碰到的第一個’\0’來標記字串的結束,因此不能儲存圖片、音訊、視訊、壓縮檔案等二進位制(bytes)儲存的內容,二進位制不安全。
    SDS的特點:
    1、不用擔心記憶體溢位問題,如果需要會對SDS進行擴容。
    2、獲取字串長度時間複雜度為O(1),因為定義了len屬性。
    3、通過"空間預分配"(sdsMakeRoomFor)和"惰性空間釋放",防止多次重分配記憶體。
    4、判斷是否結束的標誌是len屬性,可以包含’\0’(它同樣以0結尾是因為這樣就可以使用C語言中函式庫操作字串的函式了)。

  3. embstr和raw編碼的區別?為什麼要為不同大小設計不同編碼?
    embstr的使用只分配一次記憶體空間(因為RedisObject和SDS是連續的),而raw需要分配兩次記憶體空間(分別為RedisObject和SDS分配空間)。因此與raw相比,embstr的好處在於建立時少分配一次空間,刪除時少釋放一次空間,以及物件的所有資料連在一起,尋找方便。而embstr的壞處也很明顯,如果字串的長度增加需要重新分配記憶體時,整個RedisObject和SDS都需要重新分配空間,因此Redis中的embstr實現為只讀(這種編碼的內容是不能修改的)

  4. int和embstr什麼時候轉化為raw?
    1,int資料不再是整數——raw
    2,int大小超過了long的範圍(2^63-1)——embstr
    3,embstr長度超過了44個位元組——raw

  5. embstr明明沒有超過44個位元組,為什麼變成raw了?
    對於embstr,由於它的實現是隻讀的,因此在對embstr物件進行修改時,都會先轉化為raw再進行修改。因此,只要是修改embstr物件,修改後的物件一定是raw的,無論是否達到了44個位元組。

  6. 當長度小於閾值時,會還原嗎?
    關於Redis內部編碼的轉換,都符合以下規律:編碼轉換在Redis寫入資料時完成,且轉換過程不可逆,只能從小記憶體編碼向大記憶體編碼轉換(但是不包括重新set)

  7. 為什麼要對底層的資料結構使用redisObject進行一層包裝呢?
    其實無論是設計redisObject,還是對儲存字元設計這麼多的SDS,都是為了根據儲存的不同內容選擇不同的儲存方式,這樣可以實現儘量地節省記憶體空間和提升查詢速度的目的。

  8. 什麼時候使用ziplist儲存?
    當hash物件同時滿足以下兩個條件的時候,使用ziplist編碼:
    1)雜湊物件儲存的鍵值對數量<512個;
    2)所有的鍵值對的健和值的字串長度都<64byte(一個英文字母一個位元組)
    src/redis.conf配置

    hash-max-ziplist-value 64//ziplist 中最大能存放的值長度
    hash-max-ziplist-entries 512//ziplist 最多能存放t entry 節 數量
    

    如果超過這兩個閾值的任何一個,儲存結構就會轉換成hashtable.

  9. 為什麼要定義兩個雜湊表,其中一個不用呢?
    redis的hash預設使用的是ht[0],ht[1]不會初始化和分配空間。雜湊表dictht是用鏈地址法來解決碰撞問題的。在這種情況下,雜湊表的效能取決於它的大小(size屬性)和它所儲存的節點的數量(used屬性)之間的比率:
    1)比率在1:1時(一個雜湊表ht只儲存一個節點entry),雜湊表的效能最好;
    2)如果節點數量比雜湊表的大小要大很多的話(這個比例用ratio表示,5表示平均一個ht儲存5個entry),那麼雜湊表就會退化成多個連結串列,雜湊表本身的效能優勢就不再存在。
    如果單個雜湊表的節點數量過多,雜湊表的大小需要擴容。Redis裡面的這種操作叫做rehash。
    rehash的步驟:
    1)為字元ht[1]雜湊表分配空間。ht[1]的大小為第一個大於等於ht[0].used*2的2的N次方冪。比如已經使用了10000,那就是16384。
    2)將所有的ht[0]上的節點rehash到ht[1]上,重新計算hash值和索引,然後放入指定的位置。
    3)當ht[0]全部遷移到了ht[1]之後,釋放ht[0]的空間,將ht[1]設定為ht[0]表,並建立新的ht[1],為下次rehash做準備。

相關文章