前言
作為一種非關(guān)系型數(shù)據(jù)庫,redis也總是免不了有各種各樣的問題。如何有效的理解并且區(qū)分 Reids 穿透、擊穿和雪崩之間的區(qū)別,一直以來都挺困擾我的一個難題,下面將一一舉例。
(一)緩存穿透
關(guān)鍵詞:穿過 Redis 和數(shù)據(jù)庫
當 Redis 和數(shù)據(jù)庫中都沒有我們想要的數(shù)據(jù)時,就需要考慮緩存穿透的問題了。下面這段邏輯大家用的會比較多:先去 Redis 中查找某資源,Redis 中查不到就去 DB 中查,DB 中查到后回寫一份數(shù)據(jù)到 Redis 中。
舉例
對于系統(tǒng)A,假設(shè)一秒 5000 個請求,結(jié)果其中 4000 個請求是黑客發(fā)出的惡意攻擊。
黑客發(fā)出的那 4000 個攻擊,緩存中查不到,每次你去數(shù)據(jù)庫里查,也查不到。
數(shù)據(jù)庫 id 是從 1 開始的,結(jié)果黑客發(fā)過來的請求 id 全部都是負數(shù)。這樣的話,緩存中不會有,請求每次都“視緩存于無物”,直接查詢數(shù)據(jù)庫。這種惡意攻擊場景的緩存穿透就會直接把數(shù)據(jù)庫給打死。
解決方案
1、緩存空結(jié)果
如果系統(tǒng)發(fā)現(xiàn) Redis 及 DB 中都不存在該資源,就緩存空結(jié)果一段時間。需要注意哈,這次的失效時間不能設(shè)置的太長,否則數(shù)據(jù)的實效性會產(chǎn)生很大的問題。
2、用戶合法性校驗
對用戶的請求合法性進行校驗,攔截惡意重復(fù)請求。
3、布隆過濾器
看到這個名詞不要慌。簡單來說布隆過濾器的用途就是幫助你判斷某個值是否存在。舉個例子來看下:假設(shè)我們現(xiàn)在有一個長度為 9 的 bit 數(shù)組,該數(shù)組的每個位置上只能保存 1 或者 0,1 標識該位置被占用,0 標識該位置未被使用。
對于 key1,我們借助三個 Hash 函數(shù)分別對其哈希運算。
再將得到的這三個哈希值對 9 求模。
最后將這三個模值落入到 bit 數(shù)組上。
key2、key3 按照同樣的方式再處理一遍。
最后,我們會發(fā)現(xiàn)這個 bit 數(shù)組里只有位置 3 還是空著的。如果此時來了一個新的 key4 通過三個Hash算法求出的哈希值為 1、2、3,我們則可以斷定 key4 一定不存在。
布隆過濾器的原理還是比較簡單的。這里我們需要注意,布隆過濾器可能存在一定誤判的可能性,但它依然可以幫助你攔截掉大部分一定不存在的數(shù)據(jù)。
(二)緩存擊穿
關(guān)鍵詞:定點打擊
試想如果所有請求對著一個 key 照死里搞,這是不是就是一種定點打擊呢?
舉例
怎么理解呢?舉個極端的例子:比如某某明星爆出一個驚天狠料,海量吃瓜群眾同時訪問微博去查看該八卦新聞,而微博 Redis 集群中數(shù)據(jù)在此刻正好過期了,那么無數(shù)的請求則直接打到了微博系統(tǒng)的物理 DB 上,DB 瞬間掛了。
解決方案
1、熱點數(shù)據(jù)永遠不過期
比如我們可以將某個 key 的緩存時間設(shè)置為 25 小時,然后后臺有個 JOB 每隔 24 小時就去批量刷新一下熱點數(shù)據(jù)。就可以解決這個問題了。
2、使用互斥鎖
容易影響吞吐量,大部分項目設(shè)置熱點 key 永不過期就妥妥的了。
(三)緩存雪崩
關(guān)鍵詞:Redis 崩了,沒有數(shù)據(jù)了
這里的 Redis 崩了指的并不是 Redis 集群宕機了。而是說在某個時刻 Redis 集群中的熱點 key 都失效了。如果集群中的熱點 key 在某一時刻同時失效了的話,試想海量的請求都將直接打到 DB 上,DB 可能在瞬間就被打爆了。
舉例
對于系統(tǒng) A,假設(shè)每天高峰期每秒 5000 個請求,本來緩存在高峰期可以扛住每秒 4000 個請求,但是緩存機器意外發(fā)生了全盤宕機。緩存掛了,此時 1 秒 5000 個請求全部落數(shù)據(jù)庫,數(shù)據(jù)庫必然扛不住,它會報一下警,然后就掛了。此時,如果沒有采用什么特別的方案來處理這個故障,DBA 很著急,重啟數(shù)據(jù)庫,但是數(shù)據(jù)庫立馬又被新的流量給打死了。
解決方案:
1、事前
事前:redis 高可用,主從+哨兵,redis cluster,避免全盤崩潰。
2、事中
事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死。
3、事后
事后:redis 持久化,一旦重啟,自動從磁盤上加載數(shù)據(jù),快速恢復(fù)緩存數(shù)據(jù)。
(四)簡短易懂總結(jié)
最后我們再回歸到主題!如何輕松的通過聯(lián)想的方式來區(qū)分 Redis 緩存穿透、擊穿、雪崩的區(qū)別?
緩存穿透—穿過(繞過) Redis 和 DB 來搞你
緩存擊穿—定點打擊來搞你
緩存雪崩—熱點 key 在某一個時刻同時失效