1. 程式人生 > >面試之Redis

面試之Redis

有一個 動態 前綴 子進程 sorted 全量 復雜 易用 壓縮列表

面:緩存中間件——Memcached和Redis的區別是什麽?

答:Memcached的優點是簡單易用,代碼層次類似與Hash。支持簡單數據類型,但不支持數據持久化存儲,也不支持主從同步,也不支持分片。Redis的數據類型豐富,支持數據磁盤持久化存儲,支持主從,支持分片。

面:為什麽Redis能這麽快?(100000+QPS)

  • 完全基於內存,不受限於磁盤I/O,絕大部分請求是純粹的內存操作,執行效率高
  • 數據結構簡單,對數據操作也簡單(不使用表,存儲結構就是鍵值對)
  • 采用單線程(主線程,多個線程對同一個鍵做寫操作的時候,不會出現並發問題,避免了上下文的切換和鎖競爭),單線程也能處理高並發請求,想多核也可以啟動多實例
  • 使用多路I/O復用模型,非阻塞IO

面:說說你用過的Redis的數據類型?

  • String:最基本的數據類型,二進制安全
  • Hash:String元素組成的字典,適合用於存儲對象
  • List:列表,按照String元素插入順序排序
  • Set:String元素組成的無序集合,通過哈希表實現,不允許重復
  • Sorted Set(ZSet):通過分數來為集合中的成員進行從小到大的排序
  • 用於計數的HyperLogLog,用於支持存儲地理位置信息的Geo

Redis底層數據類型基礎:

  1. 簡單動態字符串
  2. 鏈表
  3. 字典
  4. 跳躍表
  5. 整數集合
  6. 壓縮列表
  7. 對象

面:如何從海量Key裏查詢出某一固定前綴的Key?

答:若使用KEYS pattern:查找出所有符合給定模式patter的key,會對使線上的業務造成卡頓(主要是一次性返回所有的滿足條件的key),此時可以使用SCAN指令(SCAN cursor [MATCH pattern] [COUNT count])無阻塞的返回一定數量的key(它是基於遊標的叠代器,需要基於上一次的遊標延續之前的叠代過程;以0作為遊標開始一次新的叠代,直到命令返回遊標0完成一次遍歷;不保證每次執行都返回某個給定數量的元素,支持模糊查詢。)

面:如何通過Redis實現簡單的分布式鎖?

分布式鎖需要解決的問題:互斥性(任意時刻只能有一個客戶端獲取鎖),安全性(鎖只能由持有該鎖的客戶端釋放),死鎖(持有鎖的客戶端宕機後,無法釋放鎖,導致其他客戶端無法獲取到該鎖導致的死鎖),容錯(當某些客戶端宕機後,其他客戶端也要能獲取鎖和釋放鎖)

答:缺陷方案:首先可以通過Redis命令SETNX key value(原子性的,如果可以不存在,則創建並賦值,時間復雜度O(1),設置成功返回1,失敗返回0),若能設置成功則說明此時沒有別的線程進入了臨界區。若失敗,則表明該資源已經被其他線程所占用了,所以需要一直等待,直到SETNX返回1即其他線程設置的key過期了(EXPIRE key seconds)。但是上述方案有個缺點:即SETNX 和 EXPIRE的復合操作不是原子性的,若某個線程執行完SETNX突然掛掉了,那麽由於沒有執行EXPIRE操作,那麽獨占資源就一直不能被其他線程所占用。

優秀方案:上述方案之所以有缺陷,是因為原子性沒有得到滿足,所以可以通過以下命令SET key value [EX secods] [PX milliseconds] [NX|XX](這條命令就是同時滿足SETNX和EXPIRE,SET操作成功時返回OK,否則返回nil)此條命令是原子操作。

面:如果有大量的Key同時過期,那麽需要註意什麽?

集中過期,由於清除大量的key會耗時,會出現短暫的卡頓現象。解決方案是在設置key的過期時間時,給每個key加上隨機值,使得過期時間分散些。

面:如何使用Redis做異步隊列?

答:使用List作為隊列,RPUSH生產消息,LPOP消費消息。但是LPOP不會等待隊列有值才消費它會一直嘗試消費,可以同過引入Sleep機制調用LPOP重試。也可通過命令BLPOP key [key...] timeout(阻塞直到隊列有消息或者超時)。缺點是:只能供一個消費者消費。如果要讓多個消費者消費,就可以使用Redis裏的pub/sub(主題訂閱者模式),但是消息的發布是無狀態的,無法保證可達(要解決這個問題,就需要使用專業的隊列Kafka)。

面:Redis如何做持久化?

  • RDB(快照)持久化:保存某個時間點的全量數據快照
    • SAVE命令:阻塞Redis的服務器進程,直到RDB文件被創建完畢
    • BGSAVE命令:Fork出一個子進程來創建RDB文件,不阻塞服務器進程
    • 缺點是:由於是內存數據的全量同步,那麽當數據量大時會由於I/O而嚴重影響性能;可能會因為Redis掛掉而丟失從當前至最近一次快照期間的數據
  • AOF(Append-Only-File)持久化:保存寫狀態。記錄除了查詢以外的所有變更數據庫狀態的指令,以append的形式追加保存到AOF文件中(增量)

面:如何解決AOF文件大小不斷增大的問題?

答:可以采取日誌重寫的方式,原理如下:

  1. 調用fork(),創建一個子進程
  2. 子進程把新的AOF寫到一個臨時文件裏,不依賴原來的AOF文件
  3. 主進程持續將新的變動同時寫到內存和原來的AOF裏(防止重寫失敗,保證數據的完整性)
  4. 主進程獲取子進程重寫AOF的完成信號往新AOF同步增量變動
  5. 使用新的AOF文件替換掉舊的AOF文件

面:當RDB和AOF文件共存的情況下,如何恢復Redis的數據?

技術分享圖片

面:談談RDB和AOF的優缺點?

  • RDB優點:全量數據快照,文件小,恢復快
  • RDB缺點:無法保存最近一次快照之後的數據
  • AOF優點:可讀性高,適合保存增量數據,數據不易丟失
  • AOF缺點:文件體積大,恢復時間長

RDB-AOF混合持久化方式:BGSAVE做鏡像全量持久化,AOF做增量持久化。

面:如何從海量數據裏快速找到所需要的數據?

答:使用分片:按照某種規則去劃分數據,使數據分散存儲在多個節點上。並且Redis為了能夠提高key的命中率,采用的是一致性hash算法(一致性hash算法:對2^32取模將hash值空間組織成虛擬的圓環,將數據key使用相同的hash函數計算出hash值,引入虛擬節點解決數據傾斜)。

參考資料

慕課網 劍指Java面試-Offer直通車

面試之Redis