目錄
Redis過期刪除和內存淘汰策略:
過期刪除策略:
內存淘汰策略(解決內存過大問題):
LRU和LFU以及他們在Redis里的實現
主從復制
哨兵模式
緩存
緩存雪崩
緩存擊穿
緩存穿透
數據庫和緩存一致性問題
Redis過期刪除和內存淘汰策略:
過期刪除策略:
當對一個key設置過期時間之后,Redis會將其加入到過期字典中,Redis里使用的是惰性刪除和定期刪除相結合的方式,惰性刪除是當查詢key的時候,先判斷是否在過期字典中存在,如果存在,則判斷過期時間和當前時間的大小,如果還未過期,返回查詢的結果,如果過期,就刪除。定期刪除每過一段時間在過期字典中隨機抽查一些key,判斷是否過期,如果過期的key占抽查的key25%以上,就會繼續抽查,直到小于25%。然后再過一段時間之后繼續抽查。
內存淘汰策略(解決內存過大問題):
當Redis的內存超過最大運行內存時,會觸發內存淘汰策略,第一種不進行數據淘汰策略,是Redis3.0版本之后的默認內存淘汰策略,當超過最大內存限制之后,不能再添加key,但是可以查詢。
兩大種:在設置了過期時間的數據中進行淘汰,1.隨機淘汰。2.優先淘汰更早的鍵值對。3.LRU淘汰算法(最近最少使用)。4.LFU淘汰算法(最近最不常用)。
在所有數據種進行淘汰,1.隨機淘汰。2.LRU。3.LFU
LRU和LFU以及他們在Redis里的實現
LRU算法是基于鏈表的結構,鏈表中的元素按照操作順序從后往前排列,最新的操作會被移動到鏈表頭部,淘汰時只需要刪除鏈表尾部的元素。
因為,傳統LRU算法需要涉及很多鏈表的移動操作,會耗時間,影響Redis緩存的性能,所以Redis用的是近似的LRU算法。是在Redis的對象結構體中添加一個額外字段,用于記錄此數據的最后一次訪問時間。當Redis進行內存淘汰時,會隨機采樣來淘汰數據,淘汰最久沒使用的那個。缺點 無法解決緩存污染問題,比如某一次讀取了大量的數據,一下就把內存占滿了,那之前的數據就會被清除掉,而這個數據如果只用一次,就會在Redis留存很長的時間,會造成緩存污染。
為了解決問題,Redis 4.0之后引入了LFU算法(最近最不常用),根據訪問次數來淘汰數據。Redis里用的是,在Redis的對象結構體中添加了數據的訪問頻次字段lru,lru 24bits,高16bit存儲ldt,記錄訪問時間戳,低8bit存儲 logc,記錄訪問頻次。然后根據這兩個值來判斷是否淘汰。
主從復制
為了解決單點故障的問題,Redis提供了主從復制模式,并且主從服務器采用的是讀寫分離方式,主服務器可以讀寫,當發生寫操作時自動把寫操作同步給從服務器,從服務器一般是只讀,并且接收主服務器同步過來的寫操作命令,然后執行命令。
第一次同步:
1.建立連接(replicaof 命令),確定主從。主服務器會返回FULLRSYNC響應命令,表示采用全量復制,把所有數據都同步給從服務器。
2.將主服務器同步數據給從服務器。主服務器生成RDB快照(異步操作,不會影響Redis處理命令)并發送給從服務器,從服務器清空所有數據然后載入RDB文件。但是同步期間發生的操作命令,主服務器會將命令寫入到 replication buffer 中,會通過第三步發送給從服務器
3.主服務器發送新寫操作給從服務器,將replication buffer中的寫操作命令發送給從服務器。
命令傳播:TCP連接,長連接。傳輸寫操作命令
分攤主服務器壓力:服務器B可以是服務器A的從服務器,也可以是服務器C的主服務器。這樣減少了主服務器生成RDB文件和傳輸文件的消耗。
增量復制:由于網絡問題,如果主從服務器間的網絡連接斷開,無法保持主從一致,Redis采用了增量復制的方式繼續同步,只會把網絡斷開期間主服務器收到的寫操作命令同步給從服務器。
首先從服務器會發送自己讀的位置,然后主服務器會在 環形緩沖區內查找位置,若找到,主服務器根據當前寫的位置和從服務器讀的位置,會將之間的增量寫入到replication buffer ,然后發送給從服務器。若未找到,主服務器會采用全量同步方式。
哨兵模式
主從架構中,當主節點掛了,那就沒有主節點來執行客戶端寫操作的請求,也不能給從節點進行數據同步了。哨兵機制能實現主從節點故障轉移(監控,選主,通知),會檢測主節點是否存活,如果發現掛了,就會選舉一個從節點切換為主節點,并且把主節點的相關信息通知給各從節點和客戶端。
監控:主觀下線,哨兵每隔一秒ping所有主從節點,如果有主從節點沒有在規定的時間響應哨兵的ping命令,哨兵就會標記為主觀下線,此時會通知所有的哨兵,其他哨兵根據自身和主節點的網絡狀況來投贊成或拒絕票。如果贊成票數到達要求后 quorum(建議為哨兵個數的二分之一加一),此時會被哨兵標記為客觀下線。
選哨兵進行主從故障轉移:哨兵標記主節點客觀下線后,該哨兵就會發起投票,其他哨兵投贊成或拒絕票。拿到半數以上贊成并且大于等于配置文件中quorum值,才能做leader。
選主:第一步:在已下線主節點(舊主節點)屬下的所有「從節點」里面,挑選出一個從節點,并將其轉換為主節點,選擇的規則:
- 過濾掉已經離線的從節點;
- 過濾掉歷史網絡連接狀態不好的從節點;
- 將剩下的從節點,進行三輪考察:優先級、復制進度、ID 號。在每一輪考察過程中,如果找到了一個勝出的從節點,就將其作為新主節點。
通知:讓已下線主節點屬下的所有「從節點」修改復制目標,修改為復制「新主節點」;
將新主節點的 IP 地址和信息,通過「發布者/訂閱者機制」通知給客戶端;
繼續監視舊主節點,當這個舊主節點重新上線時,將它設置為新主節點的從節點;
緩存
用戶請求時,利用Redis做MySQL的緩存層,Redis是內存數據庫,相當于數據緩存在內存中。雖然速度比磁盤提升幾個等級,但引入緩存層后,會出現緩存穿透、擊穿、雪崩問題。
緩存雪崩
大量緩存數據在同一時間過期,或者Redis故障宕機,此時如果有大量的用戶請求,都無法在Redis中處理,于是全部請求都直接訪問數據庫,導致數據庫宕機,造成系統崩潰。
解決辦法:
針對大量緩存數據在同一時間過期:
1.隨機設置過期時間,保證數據不會在同一個時間過期。
2.添加互斥鎖,如果發現訪問的數據不在Redis里,就加一個互斥鎖(設置過期時間。防止出現意外不釋放鎖,其他請求拿不到鎖),保證同一時間內只有一個請求來構建緩 存,當緩存構建完成后,再釋放。
Redis故障宕機:
1.通過主從節點的方式構建Redis集群。當主節點宕機,可以通過哨兵選主,避免了宕機帶來的問題。
2.啟動服務熔斷(限流。只能少部分請求)機制,暫停業務應用對緩存服務的訪問,直接返回錯誤,不用再繼續訪問數據庫,降低了對數據庫訪問的壓力,等Redis恢復正常后,再允許業務訪問。
緩存擊穿
熱點數據過期了,但是大量請求訪問這個熱點數據。緩存中沒了,就要去數據庫查詢。數據庫很容易發生崩潰。
解決方法:
1.添加互斥鎖
2.不給熱點數據添加過期時間。或者在熱點數據將要過期前,提前通知后臺更新緩存以及重新設置過期時間。
緩存穿透
請求到來時,訪問緩存沒有數據,訪問數據庫也沒有數據,無法構建緩存數據。大量的請求,導致數據庫壓力激增。
解決方法:
1.非法請求的限制。
2.發現緩存穿透現象時,可以針對查詢的數據,在緩存中設置一個空值或默認值。
3.布隆過濾器。在寫入數據時,先在布隆過濾器中做標記。等請求來時,判斷是否標記過。
數據庫和緩存一致性問題
并發情況下,先更新數據庫,再刪除緩存可以解決數據庫和緩存一致性問題。但是如果第二步操作沒有成功執行,那末還是沒作用。
解決辦法:
1.給緩存設置較短的過期時間,勉強可以解決一些問題。
2.利用消息隊列,將第二個操作加入到消息隊列。如果刪除緩存失敗,那就可以從消息隊列中重新讀取數據,然后再次刪除緩存。這個就是重試機制。