Redis及緩存雪崩、緩存穿透

本文大綱

一、簡介

二、Redis持久化方案

三、Redis的緩存雪崩,緩存穿透,緩存預熱,緩存更新,緩存降級等問題及解決方案

一 、簡介

1.Redis的特點

  • Redis是一種非關係數據庫(不會像MySQL一樣多個表直接存在直接關聯關係),也是一種內存型數據庫。

  • Redis是單線程的,並且是直接基於內存的,所以執行效率會非常高
    爲什呢Redis是單線程的呢?(經典面試題)
    官方回答 : 因爲Redis是基於內存的,所以CPU不會是Redis的瓶頸,但是內存會是,而且多線程的實現比較麻煩,直接使用單線程更省事一點(PS:這真的是官方說的,雖然說em....有一點...你懂得,但真實原因就是這麼簡單)
    有圖爲證 :


如果有興趣的話可以去看一下傳送門
因爲Redis是直接操作內存的,而且是單線程的,避免了頻繁的切換上下文,所以速度非常快

2.Redis的數據結構

  • String : 最普通和常用的一種結構,key/value
  • Hash : 類似於HashMap,底層也是哈希表
  • List : 類似於LinkedList,底層是雙向鏈表
  • Set : 類似於HashSet,於HashSet的特性一樣,不允許重複數據,不記錄元素添加順序
  • SortSet : 會自動的進行排序的set
  • Streams(流) : 18年剛剛發佈的一個新的數據結構,不是太瞭解,相關資料比較少

二 、Redis持久化方案

1.RDB(默認)

  • RDB是redis默認的持久化方式
  • RDB是採用快照的方式來進行數據持久化的,當符合快照的條件時Redis會自動對內存中的數據進行快照,然後持久化到硬盤中
  • 觸發條件
    • 符合自定義配置的快照規則
    • 執行save或者bgsave命令
      • eg : save 60 10000 :表示1分鐘內至少100個鍵被更改則進行快照。
    • 執行flushall命令
    • 執行主從複製操作
    • 在redis.conf中設置快照規則
      • 打開redis.config文件,202行,這是RDB的默認配置,滿足下面三個之一就會被觸發

      • 在247行,配置快照生成地址

      • 在237行可以配置生成快照的名稱,默認是dump.rdb

  • 每次redis啓動時都會去讀取dump.rdb快照文件,將數據加載到內存中
  • 原理 : Redis使用fork函數複製一份當前進程的副本(創建一個子進程)父進程繼續接收處理客戶端的請求,子進程負責將內存中的數據存儲到硬盤中,當子進程寫入完所有數據之後會用新的rdb文件替換舊的rdb文件
  • 細節 : 快照的時候並不會修改原有的rdb文件,而是用新生成的替換舊的,所以rdb文件是一定存在的,rdb文件是經過壓縮的二進制文件,所以佔用內存會很少,並且方便讀取
  • 優點 : 因爲是複製除了一個子進程來實現數據存儲,所以父進程還是可以繼續響應客戶端的請求,所以客戶端基基本不會受到影響,而且rdb是壓縮後的二進制文件,進行數據恢復的速度會比較快,所以rbd非常適合用來做數據備份
  • 缺點 : 必須要滿足rdb的條件纔會執行數據備份,所以有可能因爲Redis的突然宕機導致部分數據丟失,所以設置快照條件時必須足夠嚴謹

2.AOF

  • 默認情況下,AOF是處於關閉狀態的
  • 只要當前指令會修改Redis中的數據,那麼Redis就會將這條命令存儲到硬盤中,比較消耗資源,當然我們也可以通過固態硬盤等硬件來提升性能
  • 操作 :
    • 可以通過修改 redis.conf 配置文件593行中 appendonly 參數開啓 AOF 方式

    • 默認的文件名是 appendonly.aof,可以通過597行的 appendfilename 參數修改

    • 可以通過623行的appendfsync來修改寫入策略
      • everysec(默認) : 每秒寫一次
      • always : 每次有redis命令執行時就寫一次,比較安全,但是效率較低
      • no : 由系統決定什麼是後寫入,有可能導致數據丟失

    • 是否在重寫文件時進行寫入操作,645行
      • no(默認) : 會阻塞正在重寫的進程,執行寫入操作
      • yes : 不會阻塞正在重寫的進程,等到重寫完成後再進行寫入操作,性能較高

    • 設置重寫的文件增長比例以及最小內存,664行
      • percentage : 表示當前 aof 文件大小超過上一次 aof 文件大小的百分之多少的時候會進行重寫。
      • min-size : 重寫時最小的aof內存大小

    • 異常處理機制,689
      • yes(默認) : 如遇到停電等異常,在恢復之後會繼續重寫
      • no : 恢復之後直接失敗

  • 原理 :
    • Redis可以在AOF文件太大時都AOF中的命令進行重寫,重寫後的AOF文件中是恢復全部數據的最小命令合集(比如有一個key修改倆次的命令,那麼只會保留最後一次的修改命令)
    • 重寫操作是安全的,在重寫過程中並不會刪除掉舊的AOF文件,並且如果有新的命令還是會繼續加入到舊的AOF文件中,當新的AOF文件完成後會對舊的AOF文件進行替換,並且將重寫過程中新加入到舊的AOF中 的命令追加到新的AOF文件中
    • AOF文件中保存的命令都是有序的,而且都是以Redis協議的格式進行保存的,所以容易讀懂
  • AOF的修復
    • 如果Redis此時正在對AOF文件進行寫入操作,此時如果Redis突然宕機的話,AOF就會受損,Redis不會加載破損的AOF文件
    • 修復步驟 :
      • 將當前的AOF進行備份
      • 使用Redis自帶的redis-check-aof 程序,對原來的 AOF 文件進行修復.redis-check-aof --fix
      • 重啓Redis服務器,讓其重新加載AOF文件

3.RDB及AOF的選擇

  • 如果對數據的安全性要求非常高的話,那麼最好倆個一起開啓,如果能允許分鐘內的數據丟失的話,就選擇RDB
  • 如果數據需要時常備份的話,最好開啓RDB
  • 倆中可以同時開啓,也可以只使用一個,但是如果都開啓的話,Redis重啓時只會加載AOF文件來恢復數據

三 、Redis會遇到的問題以及解決方案

1.緩存雪崩

  • 發生場景 : 當Redis服務器重啓或者大量緩存在同一時期失效時,此時大量的流量會全部衝擊到數據庫上面,數據庫有可能會因爲承受不住而宕機
  • 解決方案 :
    • 均勻分佈 : 我們應該在設置失效時間時應該儘量均勻的分佈,比如失效時間是當前時間加上一個時間段的隨機值
    • 熔斷機制 : 類似於SpringCloud的熔斷器,我們可以設定閾值或監控服務,如果達到熔斷閾值(QPS,服務無法響應,服務超時)時,則直接返回,不再調用目標服務,並且還需要一個檢測機制,如果目標服務已經可以正常使用,則重置閾值,恢復使用
    • 隔離機制 : 類似於Docker一樣,當一個服務器上某一個tomcat出了問題後不會影響到其它的tomcat,這裏我們可以使用線程池來達到隔離的目的,當線程池執行拒絕策略後則直接返回,不再向線程池中增加任務
    • 限流機制 : 其實限流就是熔斷機制的一個版本,設置閾值(QPS),達到閾值之後直接返回
    • 雙緩存機制 : 將數據存儲到緩存中時存儲倆份,一份的有效期是正常的,一份的有效期長一點.不建議用這個方案,因爲比較消耗內存資源,畢竟Redis是直接存儲到內存中的

2.緩衝穿透

  • 發生場景 : 此時要查詢的數據不存在,緩存無法命中所以需要查詢完數據庫,但是數據是不存在的,此時數據庫肯定會返回空,也就無法將該數據寫入到緩存中,那麼每次對該數據的查詢都會去查詢一次數據庫
  • 解決方案 :
    • 布隆過濾 : 我們可以預先將數據庫裏面所有的key全部存到一個大的map裏面,然後在過濾器中過濾掉那些不存在的key.但是需要考慮數據庫的key是會更新的,此時需要考慮數據庫 --> map的更新頻率問題
    • 布隆過濾器(英語:Bloom Filter)是1970年由布提出的。 它實際上是一個很長的二進制向量和一系列隨機映射函數。 布隆過濾器可以用於檢索一個元素是否在一個集合中。
    • 緩存空值 : 哪怕這條數據不存在但是我們任然將其存儲到緩存中去,設置一個較短的過期時間即可,並且可以做日誌記錄,尋找問題原因

3.緩存預熱

  • 其實這個不是一個問題,是一種機制,在上線前先將需要緩存的數據放到緩存中去,這個的實現很簡單,可以在啓動的時候放(數據比較小),做一個開關(一個隱祕的接口),定時刷新緩存

4.緩存更新

  • 這也不是一個問題,是一種機制,怎麼樣保證緩存中的key是實時有效的,以及及時的更新數據資源
    • 監測機制 : 定時去監測Redis,查看過期的緩存,
      問題 :
      在看到這裏的時候我有一個問題,如果key過期了那麼我要不要再將key重新放入緩存呢,如果放入的話我設置這個有效期就完全沒有必要了,完全可以設置爲永久有效
      我想了一個解決方案,我們可以對命中率做一個記錄,如果這個key在最近一段時間內被頻繁命中的話,我們就在失效時進行更新,否則就直接清除掉
    • 被動更新 : 每次請求過來時我們判斷一下當前key是否失效,失效就重新查詢存放到緩存中,這個問題不會涉及到監測機制那個問題

5.服務降級

  • 服務降級是不得已而爲之的,在關鍵的時候丟卒保帥,保證核心功能正常運行
    • 服務拒絕 : 直接拒絕掉非核心功能的所有請求,其實基本就是直接廢棄掉某些模塊
    • 服務延遲 : 將請求加入到線程池中或隊列中,延遲執行這些請求
  • 注意 : 服務降級一定要有對應的恢復策略,不能降下去就不回來了,我們可以監測服務的狀態,當狀態適當時恢復服務的正常使用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章