【redis】redis應用場景,緩存的各種問題

redis有一個重要的應用領域——緩存

引用來自網友的圖解釋緩存在架構中的位置

默認情況下,我們的服務架構如下圖,客戶端請求service,然後service去讀取mysql數據庫

問題存在於,數據庫性能不夠用,數據庫是整個架構中最重要的一個環節,它在高併發,高寫入頻次的時候非常容易崩掉,這是一般的數據庫本身的特性所決定的,它們的架構模式註定了不可以承受較大的併發量,所以就有了緩存:

service與高速的緩存進行交互,如果緩存中有數據直接返回客戶端,如果沒有才會從MySql中去查詢。減小數據庫的壓力,提升效率,避免宕機。

例如上面章節提到的,超賣問題,有可能瞬間的流量高達上萬,我們不可能把這些請求都響應到數據庫上,這樣速度慢不說,還隨時可能宕機。

提到緩存,就不得不說下面的四大緩存名場面,幾乎是做緩存必須面對的問題。

緩存擊穿
想象一個場景,現在在一個xx辦事大廳

張三、李四、王五、趙六、錢錢、劉八、陳九 七個人正在排隊

辦事處有一個窗口,有一些自動業務機,窗口裏面的同志一下子只能接待一個人,而自動業務機因爲速度很快可以很快接待很多人。

現在,突然、自動業務機都壞了... 所有人都排到了窗口,這下忙死了窗口裏面的同志,直接撂挑子不幹了!

這個例子中,自動業務機就像是緩存,起了一個緩衝的作用,業務員就像是數據庫,處理能力比自動機器慢,而且很容易炸毛。

緩存擊穿就是這樣,當某個緩存故障、或者在高峯期緩存突然無效了,就會導致所有請求都跑到數據庫去排隊,就造成了緩存擊穿。

緩存相當於給數據庫加了一層保護能量罩,敵人進來的時候如果某個地方沒有能量,那麼如果這個地方的敵人特別多,就會導致緩存擊穿。當從緩存中查詢不到我們需要的數據就要去數據庫中查詢了。如果被黑客利用,或者高峯流量,頻繁去訪問緩存中沒有的數據,那麼緩存就失去了存在的意義,瞬間所有請求的壓力都落在了數據庫上,這樣會導致數據庫連接異常。

解決方案:

後臺設置定時任務,主動的去更新緩存數據。這種方案容易理解,就是在自動業務機旁邊加了一個維護員,壞了趕緊修好,但是機器多了就比較複雜,維護員不一定能搞得定,當key比較分散的時候,操作起來還是比較複雜的

分級緩存。什麼意思呢,就是放兩臺業務機器,平時用第一臺,第一臺壞了馬上用第二臺,用第二臺的時候修第一臺,設置兩層緩存保護層,1級緩存失效時間短,2級緩存失效時間長。有請求過來優先從1級緩存中去查找,如果在1級緩存中沒有找到相應數據,則對該線程進行加鎖,這個線程再從數據庫中取到數據,更新至1級和2級緩存。其他線程則直接從2級線程中獲取

緩存穿透
緩存穿透本質上和緩存擊穿所面臨的問題一樣,大量請求落到數據庫中。

但是出發點略有不用,緩存穿透的問題是,在高併發下,查詢一個不存在的值時,緩存不會被命中,導致大量請求直接落到數據庫上,如活動系統裏面查詢一個不存在的活動。

也就是說,緩存擊穿是當數據是存在的,但沒有被緩存到,而緩存穿透是去訪問根本不存在的值。想象一個場景,黑客截取了一個已經過期的活動的數據接口,然後不斷的去請求它,這時候有可能因爲這個活動本身已經過期了,緩存不會命中,請求就全部落地到數據庫了,這時候就造成了緩存穿透。

緩存穿透的問題解決方案也有很多

直接緩存NULL值

這個比較容易理解,就算是沒數據我也緩存一下,你下次過來命中的是空數據。

這種方法需要特別注意,爲空的值不能緩存的太久,否則有可能在真的有數據的時候影響了業務正常流程。

布隆過濾器

什麼是布隆過濾器

布隆過濾器判斷一個值不存在,那麼這個值100%不存在

布隆過濾器判斷一個值存在,這個值90%是存在的

布隆過濾器本質是一個位數組,位數組就是數組的每個元素都只佔用 1 bit 。每個元素只能是 0 或者 1。這樣申請一個 10000 個元素的位數組只佔用 10000 / 8 = 1250 B 的空間。布隆過濾器除了一個位數組,還有 K 個哈希函數。

等一下,是不是有點繞,不太好理解。

我們知道hash函數可以根據一個值生成一個對應的數字,然後與一個長度可以取模可以得到一個下標值 (你不知道?看看HashMap的實現吧)

或者你根本不知道hash是怎麼實現的,沒關係,也可以先理解下面的,我們先把這個函數假設爲 int getIndex (String value), 根據值獲取到一個下標

假設我們現在有一個數組,長度是5,每個元素的值都是0

0 , 0 , 0 , 0 , 0

現在我們數據庫中一共有五個id

a , b , c , d , e

現在我們對id們執行getIndex函數可以得到

getIndex(a) = 0

getIndex(b) = 1

getIndex(c) = 1 // 假設函數有一些誤差

getIndex(d) = 2

getIndex(e) = 3

想一想,現在來了一個新元素,f 怎麼樣判斷在id裏面存在不存在呢?

我們把開始的數組和getIndex關聯起來, 將getindex的值作爲下標,設置值爲1,數組就會變成

1 , 1 , 1 , 1 , 0

然後我們再來判斷f是否存在,假設 getIndex(f) = 4

ok了,我們只需要判斷數組裏的下標4是否是1,是1就存在,0就不存在了嘛

那如果 getIndex(f) = 2 呢? 我們開了上帝視角,很明顯f不存在呀。

布隆過濾器不能100%判斷一個元素是否真的存在數組中,但能100%判斷它不存在與數組中,這取決於hash函數的算法程度

布隆過濾器防止緩存穿透

通過對布隆過濾器的理解,我們能就過濾掉大部分的無效請求了,把數據庫中所有的id都getindex解析一次放到布隆過濾器中,請求過來的時候判斷,如果不存在就直接返回空就行了

緩存雪崩
如果緩存集中在一段時間內失效,發生大量的緩存穿透,所有的查詢都落在數據庫上,造成了緩存雪崩。

其實與緩存擊穿的理論差不多,都是突然失效導致的擊穿數據庫。

雪崩與擊穿的不同點在於雪崩強調集中失效兩個字

想象~ 我現在有三個緩存key存在redis中,過期時間是一天

一天後,由於key有可能是同時設置的緩存,導致這三個key同時失效了,即使我的緩存擊穿問題已經解決,這時候因爲集中的key失效,也會造成擊穿!,這是量級發生了改變,就像x和y的關係, x表示key的多少,y表示請求的多少。。。

解決方案

設置不同的過期時間
熱度數據
你永遠不可能每個緩存都能命中的。什麼是好的緩存策略,好的緩存策略是能夠識別熱點數據,並在熱點被讀取的時候能夠保證命中,這是一個好的緩存策略所必須的條件之一。

緩存一致性

數據庫的數據和緩存的數據是不可能一致的,數據分爲最終一致和強一致兩類。

強一致 不可以使用緩存

緩存能做的只能保證數據的最終一致性。

我們能做的只能是儘可能的保證數據的一致性。

不管是先刪庫再刪緩存 還是 先刪緩存再刪庫,都可能出現數據不一致的情況,因爲讀和寫操作是併發的,我們沒辦法保證他們的先後順序。

具體應對策略根據業務需求來制訂。

緩存過期和淘汰

Redis設置的過期時間。這個key過期時是怎麼刪除的?

Redis採用的是定期刪除,注意不是定時刪除,不可能爲每一個key做一個定時任務去監控刪除,這樣會耗盡服務器資源。

默認是每100ms檢測一次,遇到過期的key則進行刪除,這裏的檢測也不是順序檢測,而是隨機檢測。

另外爲了防止有漏網之魚,例如在100ms檢查的中間間隙,某個key過期,但同時key訪問又進來了,這時觸發 惰性刪除策略 redis會在讀取時判斷是否已經過期,過期則直接刪除。

內存淘汰是指一部分key在內存不夠用的情況下會被Redis自動刪除,從而會出現從緩存中查不到數據的情況。

例如我們的服務器內存爲2G、但是隨着業務的發展緩存的數據已經超過2G了。但是這並不能影響我們程序的運行。所以redis會從key列表中抽取一定的熱度低的數據進行淘汰策略,騰出空間存儲新的key

...持續更新

github: https://github.com/294678380/redis-lerning

原文地址https://www.cnblogs.com/ztfjs/p/redis-cache.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章