目錄
?編輯
?一、緩存問題
?1.1 緩存穿透
?1.1.1 問題來源
?1.1.2 解決方案
?1.1.2.1 緩存空對象
?1.1.2.2 使用布隆過濾器
?1.2 緩存擊穿
?1.2.1 問題來源
?1.2.2 解決方案
?1.2.2.1 設置熱點數(shù)據(jù)永遠不過期
?1.2.2.2 新增后臺定時更新緩存線程(邏輯不過期)
?1.2.2.3 使用分布式互斥鎖
?1.2.2.4 接口限流與熔斷,降級
?1.3 緩存雪崩
?1.3.1 問題來源
?1.3.2 解決方案
?1.3.2.1 緩存過期時間隨機
?1.3.2.2 分布式部署
?1.3.2.3 設置熱點數(shù)據(jù)永遠不過期
?1.3.2.4 接口限流與熔斷,降級
?二、緩存更新機制
2.1 緩存更新策略分類
?2.2 內(nèi)存淘汰機制
?2.2.1 noeviction
?2.2.2 volatile-lru
?2.2.3 volatile-lfu
?2.2.4 volatile-ttl
?2.2.5 volatile-random
?2.2.6 allkey-lru
?2.2.7 allkey-lfu
?2.2.8 allkey-random
?2.3 超時剔除
?2.3.1 定時刪除
?2.3.2 惰性刪除
?2.4 主動更新
?2.4.1 主動更新策略
?2.4.1.1 Cache Aside Pattern
?2.4.1.2 Read/Write Through Pattern
?2.4.1.3 Write Behind Caching Pattern
?2.4.2 主動更新策略需要考慮的三個問題
?2.4.1 刪除緩存還是更新緩存?
?2.4.1.1 刪除緩存
?2.4.1.2 更新緩存
?2.4.2 如何保證緩存與數(shù)據(jù)庫的操作同時成功或失???
?2.4.3 先操作緩存還是數(shù)據(jù)庫?
?2.4.3.1 先刪除緩存,再操作數(shù)據(jù)庫
2.4.3.2 先操作數(shù)據(jù)庫,再刪除緩存
2.4.3.3 延時雙刪策略
?2.5 緩存更新機制總結(jié)
?一、緩存問題
?1.1 緩存穿透
?1.1.1 問題來源
緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),而用戶不斷發(fā)起請求。由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到數(shù)據(jù)則不寫入緩存,這將導致這個不存在的數(shù)據(jù)每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。
?1.1.2 解決方案
?1.1.2.1 緩存空對象
從緩存取不到的數(shù)據(jù),在數(shù)據(jù)庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反復用同一個id暴力攻擊。
?1.1.2.2 使用布隆過濾器
類似于一個hash set,用于快速判某個元素是否存在于集合中,其典型的應用場景就是快速判斷一個key是否存在于某容器,不存在就直接返回。布隆過濾器的關鍵就在于hash算法和容器大小。
?1.2 緩存擊穿
?1.2.1 問題來源
緩存擊穿是指緩存某些熱點數(shù)據(jù)失效(一般是緩存時間到期),這時由于并發(fā)用戶特別多,同時讀緩存沒讀到數(shù)據(jù),又同時去數(shù)據(jù)庫去取數(shù)據(jù),引起數(shù)據(jù)庫壓力瞬間增大,造成過大壓力。
?1.2.2 解決方案
?1.2.2.1 設置熱點數(shù)據(jù)永遠不過期
可以在刷緩存時,設置熱點數(shù)據(jù)不過期。
?1.2.2.2 新增后臺定時更新緩存線程(邏輯不過期)
后臺新增一個緩存更新線程,緩存快要過期前刷新緩存時間,防止緩存失效。
?1.2.2.3 使用分布式互斥鎖
可以使用Redis提供的分布式互斥鎖,保證只有一個請求查詢數(shù)據(jù)庫和更新緩存,其他請求阻塞等待緩存更新完成后在訪問緩存。
?1.2.2.4 接口限流與熔斷,降級
重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級準備,當接口中的某些服務不可用時候,進行熔斷,失敗快速返回機制。
?1.3 緩存雪崩
?1.3.1 問題來源
緩存雪崩是指Redis緩存不能正常提供服務了(阻塞、服務宕機、大面積緩存失效等造成),導致所有請求都落到了數(shù)據(jù)庫上,增加了數(shù)據(jù)庫壓力或者導致數(shù)據(jù)庫宕機。
?1.3.2 解決方案
?1.3.2.1 緩存過期時間隨機
緩存數(shù)據(jù)的過期時間設置隨機,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生。
?1.3.2.2 分布式部署
采用分布式部署方式部署緩存,避免緩存服務單節(jié)點,同時將熱點數(shù)據(jù)均勻分布在不同的緩存數(shù)據(jù)庫中。
?1.3.2.3 設置熱點數(shù)據(jù)永遠不過期
可以在刷緩存時,設置熱點數(shù)據(jù)不過期。
?1.3.2.4 接口限流與熔斷,降級
重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級準備,當接口中的某些服務不可用時候,進行熔斷,失敗快速返回機制。
?二、緩存更新機制
2.1 緩存更新策略分類
內(nèi)存淘汰 | 超時剔除 | 主動更新 | |
說明 | 重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級準備,當接口中的某些服務不可用時候,進行熔斷,失敗快速返回機制。 | 給緩存數(shù)據(jù)添加TTL時間,到期后自動刪除緩存,下次查詢時更新緩存 | 編寫業(yè)務邏輯,在修改數(shù)據(jù)的同時,更新緩存 |
一致性 | 差 | 一般 | 好 |
維護成本 | 無 | 低 | 高 |
?2.2 內(nèi)存淘汰機制
?2.2.1 noeviction
不淘汰,這是默認的淘汰策略;當內(nèi)存達到限制后,寫請求(set)會返回錯誤,讀請求(get)和刪除請求(del)可以繼續(xù)進行
?2.2.2 volatile-lru
內(nèi)存不足時,在設置了過期時間的key中,優(yōu)先刪除最近最少使用的key
?2.2.3 volatile-lfu
內(nèi)存不足時,在設置了過期時間的key中,優(yōu)先刪除使用頻率最少的key
?2.2.4 volatile-ttl
內(nèi)存不足時,在設置了過期時間的key中,優(yōu)先刪除存活剩余時間最少的key
?2.2.5 volatile-random
內(nèi)存不足時,在設置了過期時間的key中,隨機刪除某個key
?2.2.6 allkey-lru
內(nèi)存不足時,在全體key范圍內(nèi),優(yōu)先刪除最近最少使用的key
?2.2.7 allkey-lfu
內(nèi)存不足時,在全體key范圍內(nèi),優(yōu)先刪除使用頻率最少的key
?2.2.8 allkey-random
內(nèi)存不足時,在全體key范圍內(nèi),隨機刪除某個key
?2.3 超時剔除
?2.3.1 定時刪除
設置一個定時任務,隨機抽取部分過期時間的key,檢查是否過期,過期了就清除掉
?2.3.2 惰性刪除
查詢獲取數(shù)據(jù)時,檢查緩存是否過期,過期則刪除,沒過期不刪除
Redis 默認采用惰性刪除+定時刪除結(jié)合的過期策略
?2.4 主動更新
?2.4.1 主動更新策略
?2.4.1.1 Cache Aside Pattern
由緩存的調(diào)用者,在更新數(shù)據(jù)庫的同時更新緩存。
?2.4.1.2 Read/Write Through Pattern
緩存和數(shù)據(jù)庫整合為一個服務,由服務來維護一致性。調(diào)用者調(diào)用服務,不用關心一致性問題。
?2.4.1.3 Write Behind Caching Pattern
調(diào)用者只操作緩存,由其他線程異步的將緩存數(shù)據(jù)持久化到數(shù)據(jù)庫,最終保持一致。
在企業(yè)中使用最多的主動更新策略是 Cache Aside Pattern。也就是我們自己編碼來保證數(shù)據(jù)的一致性。
?2.4.2 主動更新策略需要考慮的三個問題
?2.4.1 刪除緩存還是更新緩存?
?2.4.1.1 刪除緩存
更新數(shù)據(jù)庫時讓緩存失效,查詢時再更新緩存。(延遲加載)一般選擇這個方案。
這個方案比較合理一點,可以避免過多的無效寫操作,緩存刪除后,只要沒人來查詢這條數(shù)據(jù),數(shù)據(jù)就不會被寫入緩存,這樣就可以避免大量無效的寫操作
?2.4.1.2 更新緩存
每次更新數(shù)據(jù)庫都更新緩存,無效寫操作比較多。
這種方式的缺點很明顯,舉個例子:假如我更新了100次數(shù)據(jù)庫,然后又同時更新了100次緩存,但是在更新的時候并沒有人來查這個數(shù)據(jù),那么我更新這100次緩存好像也沒啥用吧,相當于前99次都是無用功,只有最后一次才是有用的。這就是無效寫操作過多的原因。
?2.4.2 如何保證緩存與數(shù)據(jù)庫的操作同時成功或失???
1)單體系統(tǒng),將緩存與數(shù)據(jù)庫操作放在一個事務中。
2)分布式系統(tǒng),利用TCC等分布式事務方案。
?2.4.3 先操作緩存還是數(shù)據(jù)庫?
?2.4.3.1 先刪除緩存,再操作數(shù)據(jù)庫
這種方式存在很明顯的問題,假設有兩個并發(fā)操作,線程A更新,線程B查詢。線程A先刪除緩存,然后還沒來得及更新數(shù)據(jù)庫,CPU資源被線程B搶走,線程B查詢緩存發(fā)現(xiàn)沒有命中(因為已經(jīng)被線程A刪除了),查詢數(shù)據(jù)庫,然后把結(jié)果寫入到緩存中。這個時候線程A終于搶到CPU資源了,然后更新數(shù)據(jù)庫,此時就會造成數(shù)據(jù)不一致問題。
2.4.3.2 先操作數(shù)據(jù)庫,再刪除緩存
這種處理方式使用的頻率是最高的,因為出錯的概率非常小,只有一種比較極端的情況才會出現(xiàn)數(shù)據(jù)一致性問題。
同樣有兩個并發(fā)請求,線程A查詢、線程B更新,當線程A查詢的時候,緩存剛好失效,然后就去查詢數(shù)據(jù)庫拿到數(shù)據(jù),在準備寫入緩存的時候,CPU資源被線程B搶走,線程B開始更新數(shù)據(jù)庫,然后刪除緩存(這一步其實等于無用,因為緩存已經(jīng)過期)。此時線程A再次獲取到CPU資源,然后寫入緩存,此時寫入的是更新前的舊數(shù)據(jù),會產(chǎn)生數(shù)據(jù)一致性問題。
看起來這確實也是一個問題,但是我們仔細分析一下這種情況都需要滿足哪些條件:
1)并發(fā)讀寫操作
2)讀緩存時,緩存剛好失效
3)寫數(shù)據(jù)庫操作要比寫緩存快
寫數(shù)據(jù)庫是操作磁盤,寫緩存是操作內(nèi)存的,所以不太可能會出現(xiàn)寫磁盤的速度快于寫內(nèi)存的。因此使用這種方式出現(xiàn)數(shù)據(jù)一致性的概率是很小的。
2.4.3.3 延時雙刪策略
?
延遲雙刪策略是分布式系統(tǒng)中數(shù)據(jù)庫存儲和緩存數(shù)據(jù)保持一致性的常用策略,但它不是強一致。其實不管哪種方案,都避免不了Redis存在臟數(shù)據(jù)的問題,只能減輕這個問題,要想徹底解決,得要用到同步鎖和對應的業(yè)務邏輯層面解決。
前面兩種方案的不足點我們進行了分析,第二種方式的使用頻率比較高,但是也有一些小缺陷,雖然說發(fā)生的概率很低,但是這個概率到了線上會不會發(fā)生也不好說,所以就有了延時雙刪策略對第二種方式做補充。
所謂延時雙刪就是先進行緩存清除,再執(zhí)行數(shù)據(jù)庫操作,最后(延遲N秒)再執(zhí)行緩存清除。延遲N秒的時間要大于一次寫操作的時間,這個延時N秒就是了完善保證第二種策略中不足,可以保證線程A的寫緩存和線程B的修改數(shù)據(jù)庫、刪除緩存都執(zhí)行完畢,然后再刪除緩存一次,就可以保證后面再來的查詢請求可以查詢到最新數(shù)據(jù)。
ps: 一般的延時時間設置為3S左右,具體情況要根據(jù)業(yè)務場景取最佳值。
?2.5 緩存更新機制總結(jié)
1)內(nèi)存淘汰:不用自己維護,利用Redis內(nèi)存淘汰機制,自動刪除部分緩存數(shù)據(jù),這些被刪除的數(shù)據(jù)在下一次被查詢時更新。這種方式一致性最差。
2)超時剔除:給緩存數(shù)據(jù)加上過期時間 ,到期后自動刪除,下次查詢時更新,數(shù)據(jù)一致性問題大概率會出現(xiàn)。維護成本比較低。
3)主動更新:編寫業(yè)務邏輯,在修改數(shù)據(jù)庫的同時更新緩存,一致性比較好,維護成本比較高。一般采用先操作數(shù)據(jù)庫再更新緩存的方式。
一般在數(shù)據(jù)一致性要求比較低的場景下可以使用內(nèi)存淘汰機制,比如商城首頁的分類信息,這些東西基本上是不會變化的。如果一致性要求比較高,我們可以采用主動更新+超時剔除兜底的方式來處理。文章來源:http://www.zghlxwxcb.cn/news/detail-521562.html
如果覺得對您有幫助,歡迎點贊+收藏+關注!文章來源地址http://www.zghlxwxcb.cn/news/detail-521562.html
到了這里,關于Redis緩存問題與緩存更新機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!