面試 - redis緩存

Redis簡介:

redis是一個使用最廣泛的緩存中間件,根據月度排行網站DB-Engines的數據,Redis是最流行的鍵值對存儲數據庫。
Redis是一個開源(BSD許可),內存存儲的數據結構服務器,可用作數據庫,高速緩存和消息隊列代理。支持字符串、哈希表、列表、集合、有序集合、位圖、HyperLogLogs等數據類型。內置複製、Lua腳本
LRU回收、事務、以及不同級別磁盤持久化功能,同事通過Redis Sentinel提供高可用,通過RedisCLuster提供自動分區。

WWH

what:是什麼、該技術是什麼、有什麼特性?
why:爲什麼,爲什麼要使用該技術,該技術解決了什麼問題?
how:怎麼做、如何使用該技術?
how:該技術是如何實現的,實現原理?

常見Redis面試題:

Redis支持哪些數據類型:

字符串、哈希表、列表、集合、有序集合

爲什麼要使用redis緩存

結合實際場景說:
訂單信息,一般是寫比較少,但是短期內後面可能會有較多的查詢。項目中使用String類型來緩存保單數據,查詢時減少了對數據庫的壓力,提高了查詢接口的QPS.

緩存和數據庫不一致問題

使用緩存的話就有雙寫的問題,涉及到雙寫問題,就會有數據不一致的情況。需要保存數據一致需要犧牲性能,不過實際場景中一般也不要求這麼高德一致性,要求嚴格一致的話,可以將讀請求和寫請求串行化,串到一個內存隊列裏去,這樣就可以保證不會出現不一致的情況。
eg:我們採取 Cache Aside Pattern

緩存模式 : Cache Aside Pattern(先淘汰緩存,再寫數據庫)

  1. 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據放入緩存,同事返回響應讀的時候;
  2. 寫的時候,先刪除緩存,然後在更新數據庫;

那麼併發情況下出現了數據不一致怎麼辦?

eg:有個寫請求,此時刪除了緩存中的數據,但是還沒來及寫數據庫;這時來了一個讀請求,又把數據庫中的舊數據load到緩存中去了,然後上一個請求的寫數據庫完成了。這時,數據庫中是最新的數據,緩存卻是舊數據了。

思路:部分請求串行化或採取補償操作

  1. 串行化:將數據庫與緩存的更新、讀取操作進行異步串行化;將相同id的讀寫請求hash到同一臺服務器處理,服務中使用隊列一個一個地執行。如果發現隊列中有對應資源的 寫請求,那麼久等待其執行結束後再去取值(需要考慮等待時間過長的問題,該方案影響較大,較爲複雜)
  2. 補償操作:既然是延遲導致的數據不一致,那麼根據數據庫實際的延遲時間,我們使用定時任務或者消息觸發,如果有寫請求結束後,我們在指定的時間之後再刪除一次該緩存值,這樣即使有不一致的髒數據,也只會出現在延遲的這一段實踐中。

緩存穿透和雪崩問題

 什麼事緩存穿透和雪崩
 何解決緩存穿透和雪崩

tip: 這兩個問題帶來的影響都是緩存失效或者未命中導致DB壓力大,從而拖垮服務。這些情況都是可能導致DB異常從而影響服務的可用性,我們着手於解決該問題即可。其實關鍵就是過濾無效的數據,增加緩存命中率,並添加對應的隔離措施以增加整個服務的可用性。

緩存穿透: 訪問了不存在的key,緩存未命中,請求會穿透到DB,量大時可能會對數據庫造成壓力導致服務異常,可能的處理方案如下:

  1. 針對這些不存在的key,可以再redis中保存對應的key和空數據,並設置較短的過期時間;
  2. 或者使用Redis Bitmap 來判斷數據是否存在已過濾無效請求

緩存雪崩:緩存同事失效或緩存服務異常,瞬時大量請求直接到達DB,對數據庫造成壓力導致服務異常,可能的處理方案如下:

  1. 保證緩存服務的高可用,採用集羣,多主多從模式部署,並開啓RDB和AOF備份,在Redis服務出現問題時能快速根據備份文件恢復緩存數據
  2. 緩存過期的時間設置隨機化
  3. 調用緩存服務時增加熔斷模塊,類似於Hystrix.

基於Redis的分佈式鎖怎麼實現?

setnx 和expire組合使用,有坑,可能有如下問題

	 1.會不會有B線程刪除了A線成加的鎖的情況
	 2.鎖超時了怎麼辦

A:我們通過setNX命令來實現分佈式鎖,設置了過期時間,不至於會出現線程異常導致鎖無法釋放的情況

Q:那釋放鎖的時候怎麼做?會不會有B線程刪除了A線程加的鎖的情況?

A:不會的,我們加鎖的時候有爲每個線程生成獨立的uuid,存儲到redis,解鎖的時候是使用Lua腳本,判斷了uuid一致纔會讓釋放對應的鎖。

Q:不錯,那鎖超時了怎麼辦呢?

A:我們可以考慮類似租約機制的實現,啓動定時任務來給線程續期,延長加鎖時間;還需要考慮實際情況。。

爲啥Redis單線程模型也能效率這麼高
Redis 基於Reactor模式開發了網絡事件處理器,叫文件事件處理器(File EventHandler).文件事件處理器是單線程的,Redis才叫做單線程的模型,採用IO多路複用機制同時監聽多個socket,根據socket上的事件來選擇對應的事件處理器處理事件,爲啥快呢:

  1. 純內存操作
  2. 核心是基於非阻塞的IO多路複用機制
  3. 單線程避免了多線程頻繁上下文切換問題
  4. 內部數據結構設計,整個的結構都類似於一個map,查找效率賊高

Redis持久化方式及區別
Q: Redis出問題後數據會全部丟是嗎?
A:不會,Redis有持久化機制,支持AOF和RDB兩種持久化方式

Q:這兩種持久化方式有什麼區別,你們使用哪種形式,怎麼配置的

A:
RDB:通過fork一個子進程保存當前內存的一個快照實現備份,適合大規模的數據恢復,數據的完整性和一致性不高,RDB可能在最後一次備份時宕機了。另外備份時會佔用內存,因爲Redis在備份時會獨立創建一個子進程,將數據寫入到一個臨時文件(此時內存中的數據時原來的兩倍),最後再將臨時文件替換之前的備份文件。
配置方式如下:

# save <指定時間間隔><執行制定次數更新操作>,滿足條件就將內存中的數據同步到硬盤中
save<seconds><changes>
#指定本地數據庫文件名,一般採用默認的dump.rdb
dbfilename dump.rdb
#默認開啓數據壓縮
rdbcompression yes

AOF : 通過每條寫入命令以 append-only的模式寫入一個日誌文件中,在Redis重啓的時候,可以通過回放AOF日誌中的寫入命令來重新構建整個數據集。AOF對數據的完整性和一致性支持更好,但是其備份日誌文件一般會比RDB方式的備份文件更大,恢復也更慢,同時由於fsync的頻率方式,會影響Redis的性能。

#打開aof
appendonly yes
#日誌文件
appendfilename "appendonly.aof"
#更新條件
#appendfsync always
appendfsync everysec
#appendfsync no
#觸發重寫的配置
#時間長了日誌會特別大,此時需要觸發重寫取內存中的數據,並重寫到一個臨時文件中。
#最後替換舊的aof文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

Redis如何實現分佈式和高可用?

A:我們採用集羣部署模式,三主三從,總共六臺服務器

Q:那Redis主從模式,數據是怎麼同步的呢?

A:
Redis主從模式原理:
Redis 2.8之前主從模式,主從之間的數據複製只有全複製機制,通過執行命令SLAVEOF ip port,使得其成爲從服務器。全數據複製流程如下:

  1. 從服務器向主服務器發送SYNC命令。
  2. 主服務器收到SYNC命令之後,執行BGSAVE命令在後臺生成RDB文件,並使用一個緩衝區記錄從現在開始執行的寫命令。
  3. 主服務器將生成的RDB文件發送給從服務器,從服務器根據RDB文件更新狀態。
  4. 主服務器將緩衝區中的寫命令發送給從服務器,從服務器執行這些命令,最終和主服務器達到一致的狀態。

命令傳播:
數據複製完成後,爲了保持一致的狀態,主服務的寫命令都需要傳播給其從服務器。

不足:
如果從服務器和主服務器斷開後,又立馬重新連接上,那麼如果還執行全同步的話就十分浪費,因爲全同步非常站cpu、io和帶寬等

Redis 2.8之後,支持PSYNC,即部分重同步機制,部分同步機制主要依靠:

  1. 主從服務器的複製偏移量:用於記錄主從服務器的狀態是否一致,以及數據複製時的偏移量
  2. 主服務器的複製積壓緩衝區:固定大小的(默認1M)FIFO隊列,用於記錄主服務器的寫命令
  3. 主服務器的ID:用於判斷,從服務器斷開連接後重新連接到的主服務器是不是原來那個

部分重同步流程:

 1. 從服務器請求同步,帶上當前的offset和之前的主服務器ID;
 2. 如果請求中的主服務ID和當前當前的主服務ID不一致,或者其offset值已經不再複製積壓緩衝區內,那麼需要執行全同步,流程同上;
 3. 否則,執行部分同步,主服務器將複製積壓緩衝區中offset之後的命令發送給從服務器;
 4. 從服務器執行對應寫命令,並修改offset,達到和主服務器狀態一致。

Redis怎麼刪除過期數據?
A:Redis有定期刪除和惰性刪除兩種

  1. 定期刪除:默認是每隔100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除
  2. 惰性刪除:在你獲取某個key的時候,Redis會檢查一下,這個key如果設置了過期時間那麼是否過期了,如果過期了此時就會刪除,不會給你返回任何東西。

Q:那如果執行了上述的刪除操作之後,Redis內存空間還是不足怎麼辦?

A:設置了過期時間的,到期後會被上述操作刪除掉,如果此時內存還是不夠的話,Redis會根據配置的策略來執行對應的操作,主要有noeviction、allkeys-lru、allkeys-random、volatile-lru、volatile-random、volatile-ttl這幾種

  1. noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,很少用。
  2. allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的,常用。
  3. allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,
  4. volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key
  5. volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key
  6. volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,優先移除更早過期時間的key
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章