1. 程式人生 > >Redis 相關面試題(下)

Redis 相關面試題(下)

(1)redis的快取失效策略和主鍵失效機制

作為快取系統都要定期清理無效資料,就需要一個主鍵失效和淘汰策略.

在Redis當中,有生存期的key被稱為volatile。在建立快取時,要為給定的key設定生存期,當key過期的時候(生存期為0),它可能會被刪除。

1.過期時間跟著key走,與值無關
在Redis中,帶有過期時間的key被稱為『易失的』(volatile)。 過期時間可以通過使用 DEL命令來刪除整個key來移除,或者被 SET和 GETSET命令覆寫(overwrite),這意味著,如果一個命令只是修改(alter)一個帶過期時間的 key的值而不是用一個新的 key值來代替(replace)它的話,那麼過期時間不會被改變。比如說,對一個 key執行 INCR命令,對一個列表進行 LPUSH命令,或者對一個雜湊表執行 HSET命令,這類操作都不會修改 key本身的過期時間。

2.設定永久有效期
使用PERSIST命令可以清除超時,使其變成一個永久的key。

3.rename命令對有效期影響
如果key被RENAME命令修改,相關的超時時間會轉移到新key上面。
如果key被RENAME命令修改,比如原來就存在Key_A,然後呼叫RENAME Key_B Key_A命令,這時不管原來Key_A是永久的還是設定為超時的,都會由Key_B的有效期狀態覆蓋。

4.重新整理過期時間
對已經有過期時間的key執行EXPIRE操作,將會更新它的過期時間。
  
5.最大快取配置
在 redis 中,允許使用者設定最大使用記憶體大小server.maxmemory預設為0,沒有指定最大快取,如果有新的資料新增,超過最大記憶體,則會使redis崩潰,所以一定要設定。redis 記憶體資料集大小上升到一定大小的時候,就會實行資料淘汰策略

。redis 提供 6種資料淘汰策略:

volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰

volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

no-enviction(驅逐):禁止驅逐資料
  注意這裡的6種機制,volatile和allkeys規定了是對已設定過期時間的資料集淘汰資料還是從全部資料集淘汰資料,後面的lru、ttl以及random是三種不同的淘汰策略,再加上一種no-enviction永不回收的策略。
  
  使用策略規則:
  1、如果資料呈現冪律分佈,也就是一部分資料訪問頻率高,一部分資料訪問頻率低,則使用allkeys-lru
  2、如果資料呈現平等分佈,也就是所有的資料訪問頻率都相同,則使用allkeys-random
  三種資料淘汰策略:
  ttl和random比較容易理解,實現也會比較簡單。主要是Lru最近最少使用淘汰策略,設計上會對key 按失效時間排序,然後取最先失效的key進行淘汰.

(2)Redis內部資料結構的實現

在Redis內部,有非常多的資料結構:sds(簡單動態字串),list,intset(整數集合),hash(字典),zskiplist(跳躍表),ziplist(壓縮表)等。
1.simple dynamic string:一種簡單動態字串,而sdshdr封裝了C原生字串,並在其基礎上,增加了一些功能,使之後對它的呼叫簡單易懂可擴充套件。

sds 的具體實現結構adshdr,len表示sds的長度,alloc表示分配了的長度,這樣方便擴充套件;free 空閒的長度;flags標誌來判斷使用哪個型別;buf[]則作為sds的真正儲存陣列。

2.list
Redis中,list的實現是一個雙端連結串列,這樣可以方便的獲取其前後的節點值,方便之後對節點的查詢;Redis通過list來對listNode進行持有,分別記錄list的頭尾節點list長度,可在O(n)的時間複雜度上進行查詢;
這裡寫圖片描述
list在Redis中運用相當廣泛,除了實現列表外,釋出和訂閱、慢查詢、監視器等功能也使用了連結串列來獲取,另外,Redis伺服器還使用連結串列來持有 多個客戶端的狀態資訊,以及用連結串列來構建客戶端輸出緩衝區。

3.dict
dictEntry是最核心的字典結構的節點結構,它儲存了key和value的內容;另外,next指標是為了解決hash衝突,字典結構的hash衝突解決方法是拉鍊法,對於hashcode重複的節點以連結串列的形式儲存。

dictht是節點dictEntry的持有者,將dictEntry結構串起來,table就是hash表,其實dictEntry *table[]這樣的書寫方式更容易理解些,size就是table陣列的長度,used標誌已有節點的數目。

dict是最外層的字典結構的介面形式,type標誌型別,privdata標誌其私有資料,dict持有兩個dictht結構,一個用來儲存資料,一個用來在rehash時使用,rehashidx標誌是否正在rehash(因為Redis中rehash是一個漸近的過程,正在rehash的時候rehashidx記錄rehash的階段,否則為-1。
這裡寫圖片描述
注:因為dictEntry節點組成的連結串列沒有子項鍊表尾部的指標,所以新加的節點一般都加在連結串列的頭部,排在已有節點的前面,因為這樣的時間複雜度為O(1)。

4.intset
當一個集合元素只有整數並且數量元素不多的時候,可以選擇用整數集合來作為其底層實現。整數集合的資料結構如上所示。
重點說一下這個contents陣列,它儲存集合中的內容,並且以從小到大的順序排列,並保證其沒有重複的元素。雖然定義中其型別為int8_t,但具體編碼方式還是取決於encoding。

當最大的數在相關範圍之內是便會對應不同的資料型別,但是如果移除了這個最大取值,不會降級。int_6, int_32,int_64

分範圍定義其型別有兩個好處:提高其靈活性,節約記憶體。但是也增加了升級的開銷。

在Redis 中,整數集合的應用範圍不是很廣,只在實現集合時用到。

5.zskiplist(跳躍表)
對於不瞭解跳躍表的可以去這個地方看看,瞭解一下:
http://blog.nosqlfan.com/html/3041.html
跳錶是一種實現起來很簡單,單層多指標的連結串列,它查詢效率很高,堪比優化過的二叉平衡樹,且比平衡樹的實現,簡單的多的多。

6.ziplist(壓縮表)
ziplist是一個編碼後的列表,是由一系列特殊編碼的連續記憶體塊組成的順序型資料結構,特殊的設計使得記憶體操作非常有效率,此列表可以同時存放字串和整數型別,列表可以在頭尾各邊支援推加和彈出操作在O(1)常量時間,但是,因為每次操作涉及到記憶體的重新分配釋放,所以加大了操作的複雜性 。
zlentry是實際儲存資料的節點。一個ziplist可以有多個zlentry節點,具體形式如下:
這裡寫圖片描述
這裡寫圖片描述
壓縮表在Redis中的應用只存在於hash和list結構的實現中,為了在儲存時節省記憶體。