Redis複習總結(Redis面試必過!!!)

注:本篇是看了別人寫的一些面試題,然後記錄一下面試題的鏈接以及自己對面試題部分知識點的理解。

 

面試題傳送門:

https://thinkwon.blog.csdn.net/article/details/103522351

https://blog.csdn.net/Butterfly_resting/article/details/89668661

 

還有一些Redis的應知應會:https://blog.csdn.net/finalheart/article/details/85230832

 

1.緩存是什麼?

緩存分爲本地緩存和分佈式緩存。  以Java爲例,guava實現的就是本地緩存,生命週期隨JVM銷燬而結束。起多個服務實例,就有多份緩存,不具有一致性。
redis和memcached這種叫分佈式緩存,多服務實例共用一份緩存數據。緩存具有一致性。

2.redis的IO多路複用?

https://draveness.me/redis-io-multiplexing/
redis採用reactor設計模式的方式來實現文件事件處理器。

文件事件處理器使用 I/O 多路複用模塊同時監聽多個 FD,當 accept、read、write 和 close 文件事件產生時,文件事件處理器就會回調 FD 綁定的事件處理器。

雖然整個文件事件處理器是在單線程上運行的,但是通過 I/O 多路複用模塊的引入,實現了同時對多個 FD 讀寫的監控,提高了網絡通信模型的性能,同時也可以保證整個 Redis 服務實現的簡單。

多個FD----->IO多路複用模塊----->文件事件分發器----->事件處理器(accept/read/write/close)

實際上是對select  epoll(linux) kqueue(macOS) evport(Solaries10)的封裝、根據不同的系統採用不同的模塊,都沒選中就用select

在這最好了解一下 epoll select 多路io複用方法。

3.Redis內存淘汰策略

maxmemory-policy volatile-lru
lru 最近最久未使用  優先被清除
ttl 對於有過期時間的,優先清除快到時間的。
random 隨機清除 (肯定不能用這玩楞啊)
noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。 這個是沒配置內存過期策略就這麼搞。

4.redis過期鍵刪除策略 

過期策略通常有以下三種:
Redis的數據結構存儲了過期時間。 過期就是執行了 del key 

定時過期:每個設置過期時間的key都需要創建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數據,對內存很友好;但是會佔用大量的CPU資源去處理過期的數據,從而影響緩存的響應時間和吞吐量。
惰性過期:只有當訪問一個key時,纔會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對內存非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,佔用大量內存。
定期過期:每隔一定的時間,會掃描一定數量的數據庫的expires字典中一定數量的key,並清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內存資源達到最優的平衡效果。(每隔100ms部分key檢查一次)
(expires字典會保存所有設置了過期時間的key的過期時間數據,其中,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis集羣中保存的所有鍵。)
Redis中同時使用了惰性過期和定期過期兩種過期策略。

5.怎麼判斷一個key是過期的呢?

redisDb結構中的expires字典中保存了數據庫中所有鍵的過期時間。

  • 判斷key是否存在於過期字典中
  • 通過過期字典拿到key的過期時間,判斷當前UNIX時間戳是否大於key時間

6.Redis解決key衝突?

鏈地址法    hash 表相當於一個hash的數組,然後每個數組位存儲成一個鏈表(hashMap就是,鏈超過8就轉紅黑樹)

Redis 的哈希表使用鏈地址法(separate chaining)來解決鍵衝突: 每個哈希表節點都有一個 next 指針, 多個哈希表節點可以用 next 指針構成一個單向鏈表, 被分配到同一個索引上的多個節點可以用這個單向鏈表連接起來, 這就解決了鍵衝突的問題。

7.redis不支持回滾?

一個Redis事務中,當 一條命令執行有問題會導致該命令失敗,後續的命令正常執行。而出現其它故障的時候(down機),執行就被終止了。redis事務是不支持回滾和原子性的、

8.Redis 存儲一個鍵值 從客戶端到磁盤的過程?

先加載到內存。然後看持久化方式,根據RDB AOF而不同。  rdb按slave配置的時間進行打快照。 aof追加命令到.rdb文件裏面


9.分佈式尋址算法?

https://www.cnblogs.com/myseries/p/10959050.html

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11

  1. hash 算法(大量緩存重建)

來了一個 key,首先計算 hash 值,然後對節點數取模。然後打在不同的 master 節點上。一旦某一個 master 節點宕機,所有請求過來,都會基於最新的剩餘 master 節點數去取模,嘗試去取數據。這會導致大部分的請求過來,全部無法拿到有效的緩存,導致大量的流量湧入數據庫。 target = hash(key)/ 質數

簡單hash算法計算的值會依賴於質數。如果再加入一個分區則之前的hash映射都會失效,無法動態調整。解決此問題的辦法爲使用一致性hash,一致性hash可以解決大部分hash引用失效的問題。

      2.一致性 hash 算法(自動緩存遷移)+ 虛擬節點(自動負載均衡) https://www.cnblogs.com/myseries/p/10959050.html

把全量的緩存空間當做一個環形存儲結構,節點和key都做hash處理放到環上,兩個節點段的key屬於順時針的節點所擁有,即使一個節點掛點,只是這一段的key歸屬於另一個順時針節點了。這段的key會重新加載數據庫。

一致性哈希算法可以將數據儘可能平均的存儲到N臺緩存服務器上,提高系統的負載均衡,並且當有緩存服務器加入或退出集羣時,儘可能少的影響現有緩存服務器的命中率,減少數據對後臺服務的大量衝擊

      3.redis cluster 的 hash slot 算法

redis cluster 有固定的 16384 個 hash slot,對每個 key 計算 CRC16 值,然後對 16384 取模,可以獲取 key 對應的 hash slot。每個節點負責維護一部分槽以及槽所映射的鍵值數據。
  redis cluster 中每個 master 都會持有部分 slot,比如有 3 個 master,那麼可能每個 master 持有 5000 多個 hash slot。hash slot 讓 node 的增加和移除很簡單,增加一個 master,就將其他 master 的 hash slot 移動部分過去,減少一個 master,就將它的 hash slot 移動到其他 master 上去。移動 hash slot 的成本是非常低的。客戶端的 api,可以對指定的數據,讓他們走同一個 hash slot,通過 hash tag 來實現。
例如:Redis Cluster 採用虛擬槽分區,所有的鍵根據哈希函數映射到 0~16383 整數槽內,計算公式:slot = CRC16(key)& 16384。每個節點負責維護一部分槽以及槽所映射的鍵值數據。
緩存的key hash結果是和slot綁定的,而不是和服務器節點綁定,所以節點的更替只需要遷移slot。(不能同一主從都掛)

10.緩存異常

1、雪崩:同一時間,大面積緩存失效,後面請求打到數據庫。例如大量的key到期。   https://juejin.im/post/5c9a67ac6fb9a070cb24bf34
解決:分散key的過期時間 或者 使用分佈式鎖來實現,分佈式鎖常採用redis來實現,簡單的就是  set resourceName value ex 5 nx 


2、穿透:緩存穿透是指用戶查詢數據,在數據庫沒有,自然在緩存中也不會有。這樣就導致用戶查詢的時候,在緩存中找不到,每次都要去數據庫再查詢一遍,然後返回空(相當於進行了兩次無用的查詢)。這樣請求就繞過緩存直接查數據庫,這也是經常提的緩存命中率問題。
解決:布隆過濾器(在緩存層之前加布隆過濾器/Redis4.0帶了,但是要小心使用)  或者如果查詢結果返回爲空,將空值進行緩存. 對於解決分佈式服務併發讀寫還是得用分佈式鎖.


3、擊穿:指一個key非常熱點,大併發集中對這個key進行訪問,當這個key在失效的瞬間,仍然持續的大併發訪問就穿破緩存,轉而直接請求數據庫。
使用互斥鎖,對於分佈式集羣應該使用分佈式鎖。

4、緩存降級
對不同功能的緩存設置熔斷機制。相當於資源就這麼多,讓次要的業務別佔資源,資源給核心業務使用。


11.什麼是布隆過濾器? https://www.cnblogs.com/cpselvis/p/6265825.html

Redis4.0帶有這個了。
解決如網頁 URL 去重、垃圾郵件識別、大集合中重複元素的判斷和緩存穿透等問題。
實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都比一般的算法要好的多,缺點是有一定的誤識別率和刪除困難。

如果不用布隆過濾器,最好的方式就是hash,但是hash雖然快,但是比較耗內存、

布隆過濾器(Bloom Filter)的核心實現是一個超大的位數組和幾個哈希函數。假設位數組的長度爲m,哈希函數的個數爲k
假設集合裏面有3個元素{x, y, z},哈希函數的個數爲3。首先將位數組進行初始化,將裏面每個位都設置位0。對於集合裏面的每一個元素,將元素依次通過3個哈希函數進行映射,每次映射都會產生一個哈希值,這個值對應位數組上面的一個點,然後將位數組對應的位置標記爲1。查詢W元素是否存在集合中的時候,同樣的方法將W通過哈希映射到位數組上的3個點。如果3個點的其中有一個點不爲1,則可以判斷該元素一定不存在集合中。反之,如果3個點都爲1,則該元素可能存在集合中。注意:此處不能判斷該元素是否一定存在集合中,可能存在一定的誤判率。可以從圖中可以看到:假設某個元素通過映射對應下標爲4,5,6這3個點。雖然這3個點都爲1,但是很明顯這3個點是不同元素經過哈希得到的位置,因此這種情況說明元素雖然不在集合中,也可能對應的都是1,這是誤判率存在的原因。

使用布隆過濾器的好處:
省空間: 不需要存儲完整的元素,只需要對元素進行hash然後將bit向量表中的某個位設爲1即可(先不考慮碰撞問題)
查找快: 只需要對查找的元素進行hash然後看bit向量表中對應的位是否爲1。
缺點:
1.因爲碰撞,會有一定的誤報率( 不在集合的一定不在, 在的不一定在 )。這個可以通過使用多個hash函數減少誤報,但無法完全消除。
2.不支持刪除操作(還是因爲碰撞,會出現誤刪的問題)。

12.如何保證緩存與數據庫雙寫時的數據一致性?

讀的時候先讀緩存,沒有讀庫再更新到緩存裏。
更新的時候,刪除緩存,並更新數據庫。下次查的時候會刷到緩存裏面。

強一致性:讀寫串行化。這樣寫的時候讀不了。

 

13.單線程的Redis

Redis利用隊列技術講併發訪問變成串行訪問。
Redis的瓶頸在於內存而不是CPU
避免了多線程的上下文切換。

14.分佈式系統下的Redis事務(redis併發競爭key)

因爲key不一定在一個分片上。Redis事務比較雞肋。
可以考慮分佈式鎖+時間戳。大家去搶鎖,搶到鎖就做set操作即可。發現自己的value的時間戳早於緩存中的時間戳,那就不做set操作了(這裏把時間戳存到緩存數據結構裏)  或者使用隊列。


15.Redis集羣  https://www.cnblogs.com/kevingrace/p/7955725.html

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11
Redis3.0自帶的Redis cluster

Redis Cluster 採用虛擬槽分區,所有的鍵根據哈希函數映射到 0~16383 整數槽內,計算公式:slot = CRC16(key)& 16383。每個節點負責維護一部分槽以及槽所映射的鍵值數據

客戶端隨機地請求任意一個Redis 實例,然後由Redis 將請求轉發給正確的Redis 節點。Redis Cluster 實現了一種混合形式的查詢路由,但並不是直接 將請求從一個Redis 節點轉發到另一個Redis 節點,而是在客戶端的幫助下直接重定向( redirected)到正確的Redis 節點。

redis cluster一般是主從的,如果一個主節點掛掉了。會將對應從節點的數據槽轉移到hash順序的主節點。某個結點掛了的話,因爲數據在其他結點上有備份,所以其他結點頂上來就可以繼續提供服務,保證了Availability。

集羣進入fail狀態的必要條件

  1. 某個主節點和所有從節點全部掛掉,我們集羣就進入faill狀態。
  2. 如果集羣超過半數以上master掛掉,無論是否有slave,集羣進入fail狀態.
  3. 如果集羣任意master掛掉,且當前master沒有slave.集羣進入fail狀態

16.Redis cluster 一個master節點down掉之後怎麼辦?

集羣是有主從的,如果down掉的節點沒有從節點,哪集羣就變成fail狀態了。
假設down掉的主節點是有從節點的,那麼由於集羣採用 hash slot算法。 將數據hash到虛擬槽,然後將虛擬槽平均分配給不同的節點。從節點的虛擬槽和主節點是一樣的,那麼主節點要是down掉,就選舉從節點作爲主節點繼續提供服務。而不是將槽亂挪。(最開始我以爲是挪槽呢)

17.Redis Cluster集羣的處理流程全梳理

在單機模式下,Redis對請求的處理很簡單。Key存在的話,就執行請求中的操作;Key不存在的話,就告訴客戶端Key不存在。然而在集羣模式下,因爲涉及到請求重定向和Slot遷移,所以對請求的處理變得很複雜,流程如下:

  1. 檢查Key所在Slot是否屬於當前Node?
  2. 計算crc16(key) % 16384得到Slot
  3. 查詢clusterState.slots負責Slot的結點指針
  4. 與myself指針比較
  5. 若不屬於,則響應MOVED錯誤重定向客戶端
  6. 若屬於且Key存在,則直接操作,返回結果給客戶端
  7. 若Key不存在,檢查該Slot是否遷出中?(clusterState.migrating_slots_to)
  8. 若Slot遷出中,返回ASK錯誤重定向客戶端到遷移的目的服務器上
  9. 若Slot未遷出,檢查Slot是否導入中?(clusterState.importing_slots_from)
  10. 若Slot導入中且有ASKING標記,則直接操作
  11. 否則響應MOVED錯誤重定向客戶端


18、Redis Cluster容錯機制failover總結  

https://www.cnblogs.com/kevingrace/p/7955725.html

failover是redis cluster的容錯機制,是redis cluster最核心功能之一;它允許在某些節點失效情況下,集羣還能正常提供服務。

redis cluster採用主從架構,任何時候只有主節點提供服務,從節點進行熱備份,故其容錯機制是主從切換機制,即主節點失效後,選取一個從節點作爲新的主節點。在實現上也複用了舊版本的主從同步機制。

從縱向看,redis cluster是一層架構,節點分爲主節點和從節點。從節點掛掉或失效,不需要進行failover,redis cluster能正常提供服務;主節點掛掉或失效需要進行failover。另外,redis cluster還支持manual failover,即人工進行failover,將從節點變爲主節點,即使主節點還活着。下面將介紹這兩種類型的failover。

1)主節點失效產生的failover
a)(主)節點失效檢測
一般地,集羣中的節點會向其他節點發送PING數據包,同時也總是應答(accept)來自集羣連接端口的連接請求,並對接收到的PING數據包進行回覆。當一個節點向另一個節點發PING命令,但是目標節點未能在給定的時限(node timeout)內回覆時,那麼發送命令的節點會將目標節點標記爲PFAIL(possible failure)。

由於節點間的交互總是伴隨着信息傳播的功能,此時每次當節點對其他節點發送 PING 命令的時候,就會告知目標節點此時集羣中已經被標記爲PFAIL或者FAIL標記的節點。相應的,當節點接收到其他節點發來的信息時, 它會記下那些被其他節點標記爲失效的節點。 這稱爲失效報告(failure report)。

如果節點已經將某個節點標記爲PFAIL,並且根據節點所收到的失效報告顯式,集羣中的大部分其他主節點(n/2+1)也認爲那個節點進入了失效狀態,那麼節點會將那個PFAIL節點的狀態標記爲FAIL。

一旦某個節點被標記爲FAIL,關於這個節點已失效的信息就會被廣播到整個集羣,所有接收到這條信息的節點都會將失效節點標記爲FAIL。

b)選舉主節點
一旦某個主節點進入 FAIL 狀態, 集羣變爲FAIL狀態,同時會觸發failover。failover的目的是從從節點中選舉出新的主節點,使得集羣恢復正常繼續提供服務。
整個主節點選舉的過程可分爲申請、授權、升級、同步四個階段:
(1)申請
新的主節點由原已失效的主節點屬下的所有從節點中自行選舉產生,從節點的選舉遵循以下條件:
a、這個節點是已下線主節點的從節點;
b、已下線主節點負責處理的哈希槽數量非空;
c、主從節點之間的複製連接的斷線時長有限,不超過 ( (node-timeout * slave-validity-factor) + repl-ping-slave-period )。

如果一個從節點滿足了以上的所有條件,那麼這個從節點將向集羣中的其他主節點發送授權請求,詢問它們是否允許自己升級爲新的主節點。
從節點發送授權請求的時機會根據各從節點與主節點的數據偏差來進行排序,讓偏差小的從節點優先發起授權請求。
(2)授權
其他主節點會遵信以下三點標準來進行判斷:
a、 發送授權請求的是從節點,而且它所屬的主節點處於FAIL狀態 ;
b、 從節點的currentEpoch〉自身的currentEpoch,從節點的configEpoch>=自身保存的該從節點的configEpoch;
c、 這個從節點處於正常的運行狀態,沒有被標記爲FAIL或PFAIL狀態;

如果發送授權請求的從節點滿足以上標準,那麼主節點將同意從節點的升級要求,向從節點返回CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK授權。
(3)升級
一旦某個從節點在給定的時限內得到大部分主節點(n/2+1)的授權,它就會接管所有由已下線主節點負責處理的哈希槽,並主動向其他節點發送一個PONG數據包,包含以下內容:
a、 告知其他節點自己現在是主節點了
b、 告知其他節點自己是一個ROMOTED SLAVE,即已升級的從節點;
c、告知其他節點都根據自己新的節點屬性信息對配置進行相應的更新
(4)同步
其他節點在接收到ROMOTED SLAVE的告知後,會根據新的主節點對配置進行相應的更新。特別地,其他從節點會將新的主節點設爲自己的主節點,從而與新的主節點進行數據同步。
至此,failover結束,集羣恢復正常狀態。

此時,如果原主節點恢復正常,但由於其的configEpoch小於其他節點保存的configEpoch(failover了產生較大的configEpoch),故其配置會被更新爲最新配置,並將自己設新主節點的從節點。

另外,在failover過程中,如果原主節點恢復正常,failover中止,不會產生新的主節點。

2)Manual Failover
Manual Failover是一種運維功能,允許手動設置從節點爲新的主節點,即使主節點還活着。
Manual Failover與上面介紹的Failover流程大都相同,除了下面兩點不同:
a)觸發機制不同,Manual Failover是通過客戶端發送cluster failover觸發,而且發送對象只能是從節點;
b)申請條件不同,Manual Failover不需要主節點失效,failover有效時長固定爲5秒,而且只有收到命令的從節點纔會發起申請。

另外,Manual Failover分force和非force,區別在於:非force需要等從節點完全同步完主節點的數據後才進行failover,保證不丟失數據,在這過程中,原主節點停止寫操作;而force不進行進行數據完整同步,直接進行failover。

3)集羣狀態檢測
集羣有OK和FAIL兩種狀態,可以通過CLUSTER INFO命令查看。當集羣發生配置變化時, 集羣中的每個節點都會對它所知道的節點進行掃描,只要集羣中至少有一個哈希槽不可用(即負責該哈希槽的主節點失效),集羣就會進入FAIL狀態,停止處理任何命令。
另外,當大部分主節點都進入PFAIL狀態時,集羣也會進入FAIL狀態。這是因爲要將一個節點從PFAIL狀態改變爲FAIL狀態,必須要有大部分主節點(n/2+1)認可,當集羣中的大部分主節點都進入PFAIL時,單憑少數節點是沒有辦法將一個節點標記爲FAIL狀態的。 然而集羣中的大部分主節點(n/2+1)進入了下線狀態,讓集羣變爲FAIL,是爲了防止少數存着主節點繼續處理用戶請求,這解決了出現網絡分區時,一個可能被兩個主節點負責的哈希槽,同時被用戶進行讀寫操作(通過禁掉其中少數派讀寫操作,證保只有一個讀寫操作),造成數據丟失數據問題。
說明:上面n/2+1的n是指集羣裏有負責哈希槽的主節點個數。

 

19.Redis中的pipeline

管道嘛,肯定是傳輸用的。  發送命令-〉命令排隊-〉命令執行-〉返回結果

管道(pipeline)可以一次性發送多條命令並在執行完後一次性將結果返回,pipeline通過減少客戶端與redis的通信次數來實現降低往返延時時間

  • 原生批命令mget是原子性,pipeline是非原子性
  • 原生批命令一命令多個key, 但pipeline支持多命令(存在事務),非原子性
  • 原生批命令是服務端實現,而pipeline需要服務端與客戶端共同完成

 

20.redis的底層數據結構

 

 

 

參考:https://draveness.me/redis-io-multiplexing/   io多路複用

https://www.cnblogs.com/myseries/p/10959050.html    hash尋址

https://juejin.im/post/5c9a67ac6fb9a070cb24bf34        緩存異常

https://www.cnblogs.com/cpselvis/p/6265825.html       布隆過濾器

https://www.cnblogs.com/kevingrace/p/7955725.html   Redis集羣!

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11          Redis集羣

 

 

 

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