Redis常見知識點

Redis單線程爲什麼執行速度這麼快?

  1. 純內存操作,避免了大量訪問數據庫,減少直接讀取磁盤數據。Redis將數據存儲在內存裏面,讀寫數據的時候不會收到硬盤I/O速度的限制,所以速度更快;
  2. 單線程操作:避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現的死鎖而導致的性能消耗
  3. 採用了非阻塞I/O多路複用機制

Redis數據結構底層實現 

☞ String

simple dynamic string (SDS)數據結構

struct sdshdr{
 //記錄buf數組中已使用字節的數量
 //等於 SDS 保存字符串的長度
 int len;
 //記錄 buf 數組中未使用字節的數量
 int free;
 //字節數組,用於保存字符串
 char buf[];
}

優點:

  1. 不會出現字符串變更造成的內存溢出問題
  2. 獲取字符串長度時間複雜度爲1
  3. 空間預分配,惰性空間釋放free字段,會默認留夠一定的空間防止多次重分配內存

應用場景:String緩存結構體用戶信息,計數

☞ Hash

數組+鏈表的基礎上,進行了一些Hash優化

  1. Redis的Hash採用鏈地址法來處理衝突,沒有使用紅黑樹優化
  2. 哈希表節點採用單鏈表結構
  3. rehash優化(採用分而治之的思想,將龐大的遷移工作量劃分到每一次CURD中,避免了服務繁忙)

應用場景:保存結構體信息可部分獲取不用序列化所有字段

☞ List

List的實現爲一個雙向鏈表,既可以支持反向查找和遍歷

應用場景:如博客的關注列表,粉絲列表等都可以用Redis的List結構來實現

☞ Set

內部實現是一個value爲null的HashMap,實際就是通過計算Hash的方法來快速排序的,這也是set能提供判斷一個成員是否在集合內的原因。

應用場景:交集(sinter)、並集(sunion)、差集(sdiff),實現如共同關注、共同喜好、二度好友等功能。

☞ Zset

內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是所有的成員,排序依據是HashMap裏存的score,使用跳躍表的結構可以獲得比較高的查找效率,並在實現上比較簡單。跳錶:每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。

應用場景:實現延時隊列

Redis事務

  1. Multi開啓事務
  2. Exec執行事務塊內命令
  3. Discard取消事務
  4. Watch監視一個或多個key,如果事務執行前key被改動,事務將打斷

Redis事務的實現特徵

  1. 所有命令都會被串行化的順序執行,事務執行期間,Redis不會再爲其他客戶端的請求提供任何服務,從而保證了事務中的所有命令被原子地執行
  2. Redis事務中如果有一條命令執行失敗,其後地命令仍然會被繼續執行
  3. 在事務開啓之前,如果客戶端與服務端之間出現通訊故障並導致網絡斷開,其後所有待執行的語句都不會被服務器執行。然而如果網絡中斷事件是發生客戶端執行EXEC命令之後,那麼該事務中的所有命令都會被服務器執行
  4. 當使用Append-Only模式時,Redis會通過調用系統函數write將事務內的所有寫操作在本次調用中全部寫入磁盤;然而如果在寫入的過程中出現系統崩潰,如電源故障導致的宕機,那麼此時也許只有部分數據被寫入到磁盤,而另一部分數據卻已經丟失。Redis服務器會在重新啓動時執行一系列必要的一致性檢測,一旦發現類似問題,就會立即退出並給出相應的錯誤提示。此時,我們要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到數據不一致的錯誤,並將已經寫入的部分數據進行回滾。修復之後我們就可以再次重啓Redis服務器了。

Redis同步機制

☞ 全量拷貝

  1. slave第一次啓動時,連接Master,發送PSYNC命令
  2. master會執行bgsave命令來生成rdb文件,期間的所有寫命令將被寫入緩衝區
  3. master bgsave執行完畢,向slave發送rdb文件
  4. slave收到rdb文件,丟棄所有舊數據,開始載入rdb文件
  5. rdb文件同步結束後,slave執行從master緩衝區發送過來的所有寫命令
  6. 此後master沒執行一個寫命令,就向slave發送相同的寫命令

☞ 增量拷貝

如果出現網絡閃斷或者命令丟失等異常情況,從節點之前保存了自身已複製的偏移量和主節點的運行ID,主節點根據偏移量把複製積壓緩存裏的數據發送給從節點,保證主從複製進入正常狀態。

Redis集羣模式性能優化

  1. Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件
  2. 如果數據比較重要,某個slave開啓AOF備份數據,策略設置爲每秒同步一次
  3. 爲了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內
  4. 儘量避免在壓力很大的主庫上增加從庫
  5. 主從複製不要用圖狀結構,用單向鏈表結構更爲穩定,即:Master <- Slave1 <- Slave2 <- Slave3...這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立即啓用Slave1做Master,其他不變。

Redis集羣方案

  1. 官方cluster方案
  2. twemproxy
  3. codis

集羣不可用場景

  1. Master掛掉,且當前master沒有slave
  2. 集羣超過半數以上Master掛掉,無論是否有Slave集羣進入Fail狀態

Redis最適合的場景

  1. 會話緩存session cache
  2. 排行榜/計數器ZRANGE
  3. 發佈/訂閱

緩存淘汰策略

  1. 先進先出算法(FIFO)
  2. 最近最少使用(Least Frequently Used, LFU)
  3. 最長時間未被使用(Least Recently Used, LRU)

當存在熱點數據時,LRU的效率很好,但偶發性的、週期性的批量操作會導致LRU命中率急劇下降,緩存污染情況比較嚴重

緩存雪崩以及處理方法

☞ 產生原因

同一時刻大量緩存失效

☞ 處理方法

  1. 緩存數據增加過期標記
  2. 設置不同的緩存失效時間
  3. 雙層緩存策略,C1爲短期,C2爲長期
  4. 定時更新策略

緩存擊穿原因以及處理辦法

☞ 產生原因

頻繁請求查詢系統中不存在的數據導致

☞ 處理方法

  1. cache null策略,查詢反饋結果爲null仍然緩存這個null結果,設置不超過5分鐘的過期時間
  2. 布隆過濾器,所有可能存在的數據映射到足夠大的bitmap中
    1. google過濾器過濾器:基於內存,重啓失效不支持大數據量,無法應用在分佈式場景
    2. redis布隆過濾器:可擴展性,不存在重啓失效問題,需要網絡io,性能低於google布隆過濾器

Redis阻塞原因

  1. 數據結構使用不合理bigkey
  2. CPU飽和
  3. 持久化阻塞,rgb fork子線程,aof每秒刷盤等 

hot key出現造成集羣訪問量傾斜解決辦法

  1. 使用本地緩存
  2. 利用分片算法的特性,對key進行打散處理(給hot key加上前綴後者後綴,把一個hotkey的數量變成redis實例個數N的倍數M,從而由訪問一個redis key變成訪問N*M個redis key)

Redis如何做持久化

bgsave做鏡像全量持久化,aof做增量持久化。因爲bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失數據,所以需要aof來配合使用。在redis實例重啓時,會使用bgsave持久化文件重新構建內存,再使用aof重放近期的操作指令來實現完整重啓之前的狀態。

bgsave的原理是什麼?

fork和cow。fork是指redis通過創建子線程來進行bgsave操作,cow是指copy on write,子線程創建後,父子線程共享數據段,父線程繼續提供讀寫服務,寫進的頁面數據會逐漸和子線程分離開來。

RDB和AOF區別

  1. RDB文件格式緊湊,方便數據恢復,保存rdb文件時父線程會fork出子線程由其完成具體持久化操作,最大化redis性能,恢復大數據集速度更快,只有手動提交save命令或關閉命令時才觸發備份操作;
  2. AOF記錄服務器的每次寫操作(默認1s寫入一次),保存數據更完整,在redis重啓時會重放這些命令來恢復數據,操作效率高,故障丟失數據更少,但是文件體積更大

1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如何將它們全部找出來?

  • 使用keys指令可以掃出指定模式的key列表,如果這個redis正在給線上的業務提供服務,那使用keys指令會存在問題:因爲redis是單線程的,keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。
  • 這個時候可以使用scan指令,scan指令可以無阻塞地提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體花費的時間會比直接用keys指令長。

如何使用Redis做異步隊列?

一般使用List結構最爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會兒再試;List還有個指令叫blpop,在沒有消息的時候,它會阻塞直到消息到來。

Redis如何實現延時隊列?

使用sortedset,想要執行時間的時間戳作爲score,消息內容作爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。

爲啥redis zset使用跳躍鏈表而不是紅黑樹實現?

  1. skiplist的複雜度和紅黑樹一樣,而且實現起來更簡單
  2. 在併發環境下紅黑樹在插入和刪除時需要rebalance,性能不如調錶

 

 

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