本文大綱
一、簡介
二、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 : 由系統決定什麼是後寫入,有可能導致數據丟失
- 可以通過623行的appendfsync來修改寫入策略
-
- 是否在重寫文件時進行寫入操作,645行
- no(默認) : 會阻塞正在重寫的進程,執行寫入操作
- yes : 不會阻塞正在重寫的進程,等到重寫完成後再進行寫入操作,性能較高
- 是否在重寫文件時進行寫入操作,645行
-
- 設置重寫的文件增長比例以及最小內存,664行
- percentage : 表示當前 aof 文件大小超過上一次 aof 文件大小的百分之多少的時候會進行重寫。
- min-size : 重寫時最小的aof內存大小
- 設置重寫的文件增長比例以及最小內存,664行
-
- 異常處理機制,689
- yes(默認) : 如遇到停電等異常,在恢復之後會繼續重寫
- no : 恢復之後直接失敗
- 異常處理機制,689
- 原理 :
- 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年由布隆提出的。 它實際上是一個很長的二進制向量和一系列隨機映射函數。 布隆過濾器可以用於檢索一個元素是否在一個集合中。
- 緩存空值 : 哪怕這條數據不存在但是我們任然將其存儲到緩存中去,設置一個較短的過期時間即可,並且可以做日誌記錄,尋找問題原因
- 布隆過濾 : 我們可以預先將數據庫裏面所有的key全部存到一個大的
3.緩存預熱
- 其實這個不是一個問題,是一種機制,在上線前先將需要緩存的數據放到緩存中去,這個的實現很簡單,可以在啓動的時候放(數據比較小),做一個開關(一個隱祕的接口),定時刷新緩存
4.緩存更新
- 這也不是一個問題,是一種機制,怎麼樣保證緩存中的key是實時有效的,以及及時的更新數據資源
- 監測機制 : 定時去監測Redis,查看過期的緩存,
問題 :
在看到這裏的時候我有一個問題,如果key過期了那麼我要不要再將key重新放入緩存呢,如果放入的話我設置這個有效期就完全沒有必要了,完全可以設置爲永久有效
我想了一個解決方案,我們可以對命中率做一個記錄,如果這個key在最近一段時間內被頻繁命中的話,我們就在失效時進行更新,否則就直接清除掉 - 被動更新 : 每次請求過來時我們判斷一下當前key是否失效,失效就重新查詢存放到緩存中,這個問題不會涉及到監測機制那個問題
- 監測機制 : 定時去監測Redis,查看過期的緩存,
5.服務降級
- 服務降級是不得已而爲之的,在關鍵的時候丟卒保帥,保證核心功能正常運行
- 服務拒絕 : 直接拒絕掉非核心功能的所有請求,其實基本就是直接廢棄掉某些模塊
- 服務延遲 : 將請求加入到線程池中或隊列中,延遲執行這些請求
- 注意 : 服務降級一定要有對應的恢復策略,不能降下去就不回來了,我們可以監測服務的狀態,當狀態適當時恢復服務的正常使用