Redis 缓存穿透、雪崩、缓存数据库不一致、持久化方式、分布式锁、过期策略
1. Redis 緩存穿透
1.1 Redis 緩存穿透概念
訪問了不存在的 key,緩存未命中,請求會穿透到 DB,量大時可能會對 DB 造成壓力導致服務異常。
由于不恰當的業務功能實現,或者外部惡意攻擊不斷地請求某些不存在的數據內存,由于緩存中沒有保存該數據,導致所有的請求都會落到數據庫上,對數據庫可能帶來一定的壓力,甚至崩潰。
1.2 Redis 緩存穿透解決方案
- 針對這些不存在的 key,可以在 Redis 中保存對應的 key 和空數據,并設置較短的過期時間;
- 或者使用 Redis Bitmap 來判斷數據是否存在以過濾無效請求;
2. Redis 緩存雪崩
2.1 Redis 緩存雪崩概念
緩存同時失效或緩存服務異常,瞬時大量請求直接到達 DB,對 DB 造成壓力導致服務異常,
2.2 Redis 緩存雪崩解決方案:
- 保證緩存服務的高可用,采用集群,多主多從模式部署,并開啟 RDB 和 AOF 備份,在 Redis 服務出問題時能快速根據備份文件恢復緩存數據;
- 緩存過期時間的設置隨機化;
- 調用緩存服務時增加熔斷模塊,類似 Hystrix;
總的來說,緩存穿透和雪崩帶來的影響都是緩存失效或未命中導致 DB 壓力大,從而可能拖垮服務。這些情況都是可能導致 DB 異常從而影響服務的可用性。
其實關鍵就是過濾無效的數據,增加緩存命中率,并添加對應的隔離措施以增加整個服務的可用性。
3. Redis 緩存和數據庫不一致問題
使用了緩存的話,就有雙寫問題。通常,涉及到雙寫問題,就會有數據不一致的情況。需要保存數據一致需要犧牲性能,不過實際場景中一般也不要求這么高的一致性。要求嚴格一致的話,可以將讀請求和寫請求串行化,串到一個內存隊列里去,這樣就可以保證一定不會出現不一致的情況。
緩存模式:Cache Aside Pattern(先淘汰緩存,再寫數據庫):
- 讀的時候,先讀緩存,緩存沒有的話,那么就讀數據庫,然后取出數據后放入緩存,同時返回響應讀的時候;
- 寫的時候,先更新數據庫,然后再刪除緩存。
先看一下數據的讀取過程,規則是“先讀 cache,再讀 db”,詳細步驟如下:
-
每次讀取數據,都從 cache 里讀;
-
如果讀到了,則直接返回,稱作 cache hit;
-
如果讀不到 cache 的數據,則從 db 里面撈一份,稱作 cache miss;
-
將讀取到的數據塞入到緩存中,下次讀取時,就可以直接命中。
再來看一下寫請求,規則是“先更新 db,再刪除緩存”,詳細步驟如下:
-
將變更寫入到數據庫中;
-
刪除緩存里對應的數據。
3.1 并發情況下出現了數據不一致問題怎么辦?
eg. 有個寫請求,此時刪除了緩存中的數據,但是還沒來得及寫數據庫;這個時候來了一個讀請求,又把數據庫中的舊數據 load 到緩存中去了,然后上一個請求的寫數據庫操作完成了。這個時候,數據庫中的是最新的數據,緩存中的卻是舊數據了.
解決思路:部分請求串行化或采取補償操作。
- 串行化:將數據庫與緩存更新與讀取操作進行異步串行化;將相同 id 的讀寫請求 hash 到同一臺服務處理,服務中使用隊列一個一個地執行。如果發現隊列中有對應資源的寫請求,那么就等待其執行結束后再去取值返回(這里需要考慮等待時間過長的問題,還有該方案影響較大,較復雜)。
- 補償操作:既然是延遲導致的數據不一致,那么根據數據庫實際的延遲時間,我們使用定時任務或者消息觸發,如果有寫請求結束后,我們在指定的時間之后再刪除一次該緩存值,這樣即使有不一致的臟數據,那也只會出現在延遲的這一段時間中。
4. 基于 Redis 的分布式鎖是怎么實現的?
分布式鎖實現要保證幾個基本點。
互斥性:任意時刻,只有一個資源能夠獲取到鎖。
容災性:能夠在未成功釋放鎖的的情況下,一定時限內能夠恢復鎖的正常功能。
統一性:加鎖和解鎖保證同一資源來進行操作。
示例:我們通過 SETNX 命令來實現的分布式鎖,設置了過期時間,不至于會出現線程異常導致鎖無法釋放的情況。
5. 為啥 Redis 單線程模型也能效率這么高?
Redis 基于 Reactor 模式開發了網絡事件處理器,這個處理器叫做文件事件處理器(File Event Handler)。這個文件事件處理器是單線程的,Redis 才叫做單線程的模型,采用 IO 多路復用機制同時監聽多個 Socket,根據 Socket 上的事件來選擇對應的事件處理器來處理這個事件,為啥快呢:
- 純內存操作
- 核心是基于非阻塞的 IO 多路復用機制
- 單線程反而避免了多線程的頻繁上下文切換問題)
- 內部數據結構設計,整個的結構都類似于一個 map,查找效率賊高
Redis是單線程處理網絡指令請求,所以不需要考慮并發安全問題。所有的網絡請求都是一個線程處理。但不代表所有模塊都是單線程。
6. Redis 持久化方式及其區別
Redis 有持久化機制的,它支持 AOF 和 RDB 兩種持久化方式。
RDB:通過 fork 一個子進程保存當前內存的一個快照實現備份. 適合大規模的數據恢復,但是數據的完整性和一致性不高,因為 RDB 可能在最后一次備份時宕機了。另外備份時會占用內存,因為 Redis 在備份時會獨立創建一個子進程,將數據寫入到一個臨時文件(此時內存中的數據是原來的兩倍哦),最后再將臨時文件替換之前的備份文件。配置方法如下:
# save <指定時間間隔> <執行指定次數更新操作>,滿足條件就將內存中的數據同步到硬盤中save <seconds> <changes> # 指定本地數據庫文件名,一般采用默認的 dump.rdbdbfilename dump.rdb # 默認開啟數據壓縮rdbcompression yes
AOF: 通過對每條寫入命令以 append-only 的模式寫入一個日志文件中,在 Redis 重啟的時候,可以通過回放 AOF 日志中的寫入指令來重新構建整個數據集。 AOF 對數據數據的完整性和一致性支持更好,但是其備份日志文件一般會比 RDB 方式的備份文件更大,恢復也更慢,同時由于 fsync 的頻率方式,會影響 Redis 的性能。
# 打開aofappendonly yes # 日志文件appendfilename "appendonly.aof" # 更新條件# appendfsync alwaysappendfsync everysec# appendfsync no# 觸發重寫的配置# 時間長了日志會特別大,此時需要觸發重寫# 重寫的原理:Redis 會fork出一條新進程,讀取內存中的數據,并重新寫到一個臨時文件中。# 最后替換舊的aof文件。auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb
后續:Redis 4.0 之后,持久化增加了混合 RDB-AOF 持久化格式。具體可以參考
http://blog.huangz.me/diary/2016/redis-4-outline.html
7. Redis 如何實現分布式和高可用?
Redis 主從模式原理:
Redis 2.8 之前主從模式,主從之間的數據復制只有全復制機制,通過執行命令 SLAVEOF ip port,使得其成為從服務器。全數據復制流程如下:
從服務器向主服務器發送 SYNC 命令。
主服務器收到 SYNC 請求后,執行 BGSAVE 命令在后臺生成 RDB 文件,并使用一個緩沖區記錄從現在開始執行的寫命令。
主服務器將生成的 RDB 文件發送給從服務器,從服務器根據RDB文件更新狀態。
主服務器將緩沖區中的寫命令發送給從服務器,從服務器執行這些命令,最終和主服務器達到一致的狀態。
命令傳播
數據復制完成后,為了保持一致的狀態,主服務的寫命令都需要傳播給其從服務器。
不足:上面的方式存在不足,即如果從服務器是和主服務器斷開后,又立馬重新連接上后。那么此時如果還執行全同步的話,就十分浪費。因為全同步非常占用 CPU、IO 和帶寬等。
Redis 2.8 之后,支持了 PSYNC,即部分重同步機制。部分重同步機制主要依靠:
主從服務器的復制偏移量: 用于記錄主從服務器的狀態是否一致,以及數據復制時的偏移量
主服務器的復制積壓緩沖區:固定大小的(默認 1M)FIFO 隊列,用于記錄主服務器的寫命令
主服務器的 ID:用于判斷,從服務器斷開連接后重新連接到的主服務器還是不是原來那個
部分重同步流程:
主從服務器都有一個復制偏移量,當執行了寫命令時,復制偏移量對應會增加。
從服務器請求同步,帶上當前的 offset 和之前的主服務器 ID;
如果請求中的主服務 ID 和當前的主服務器ID不一致,或者其 offset 的值已經不再復制積壓緩沖區內,那么需要執行全同步,流程同上;
否則,執行部分同步,主服務器將復制積壓緩沖區中 offset 之后的命令發送給從服務器;
從服務器執行對應寫命令,并修改 offset,達到和主服務器狀態一致。
8. Redis 的過期策略都有哪些?
8.1 定時刪除策略
在設置 key 的過期時間的同時,為該 key 創建一個定時器,讓定時器在 key 的過期時間來臨時,對 key 進行刪除。
- 優點:保證內存盡快釋放。
- 缺點:若 key 過多,刪除這些 key 會占用很多 CPU 時間, 而且每個 key 創建一個定時器,性能影響嚴重。
8.2 惰性刪除策略
key 過期的時候不刪除,每次從數據庫獲取 key 的時候去檢查是否過期,若過期,則刪除,返回 null。
- 優點:CPU 時間占用比較少。
- 缺點:若 key 很長時間沒有被獲取, 將不會被刪除,可能造成內存泄露
8.3 定期刪除策略
每隔一段時間執行一次刪除(在 redis.conf 配置文件設置 hz,1s 刷新的頻率)過期 key 操作。
-
優點:可以控制刪除操作的時長和頻率,來減少 CPU 時間占用,可以避免惰性刪除時候內存泄漏的問題。
-
缺點:
對內存友好方面,不如定時策略
對 CPU 友好方面,不如惰性策略
那如果執行了上述的刪除操作后,Redis 的內存空間還是不足怎么辦呢?
設置了過期時間的,到期后會被上述操作刪除掉;如果此時內存還是不夠的話,Redis 會根據配置的策略來執行對應的操作,主要有 noeviction、allkeys-lru、allkeys-random、volatile-lru、volatile-random、volatile-ttl 這幾種,balabalabala…
- noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太惡心了。
- allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的 key(這個是最常用的)。
- allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的 key 給干掉啊。
- volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的 key(這個一般不太合適)。
- volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個 key。
- volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的 key 優先移除。
部分內容參考以下鏈接
https://gitbook.cn/books/5d07228c2df51311ff3a6498/index.html
https://gitbook.cn/books/5c97654679ca930d56250d14/index.html
總結
以上是生活随笔為你收集整理的Redis 缓存穿透、雪崩、缓存数据库不一致、持久化方式、分布式锁、过期策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker | Docker技术基础梳
- 下一篇: qq个性签名薛之谦