1. 程式人生 > >【redis】redis應用場景,快取的各種問題

【redis】redis應用場景,快取的各種問題

如果你還不知道redis的基本命令與基本使用方法,請看 

【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-ler