1. 程式人生 > 資料庫 >高併發下redis快取常見問題

高併發下redis快取常見問題

redis作為快取,與資料庫一起給系統提供資料服務,redis不只是提供了查詢訪問的高效能響應,而且遮蔽了大量的對資料庫的查詢請求,避免了高併發下資料庫被擊潰的問題。

對於應用了快取的資料,可以粗略的理解為在應用和資料中間加了一層。但是正所謂寶劍雙鋒,在增加快取層後,解決了很多問題,但也不可避免的帶來一些技術複雜度,尤其是在高併發的場景下。下面我們來一起看看有那些常見問題和對應的解決方案。

資料一致模式

快取層和DB層的資料一致處理模式《 cache aside pattern 》分為兩種:

讀:資料查詢的時候,先從redis中獲取,沒有則查詢資料庫,結果寫入redis快取,返回結果。

寫:資料寫入的時候,先寫DB,再失效快取:

具體如下圖:

在這裡插入圖片描述

常見問題

快取穿透

問題產生

當用戶高併發大量請求不存在的key時,因為redis沒有快取該key的資料,所有查詢會穿透快取,直接指向資料庫,資料庫的連線數會急速上升,很快就會導致連線耗盡,資料庫宕機。

解決方案

解決該問題的思路是:遮蔽大量查詢連線資料庫。大致可以有以下3中方案:

1,遮蔽少量IP密集性高併發訪問,通過nginx配置 HttpLimitReqModul和HttpLimitZoneModule來限制ip在同一時間段的訪問次數,以避免惡意攻擊的快取穿透。

2,快取空值,對於提供快取的資料,可以在資料庫查詢不到資料的情況下,在快取中增加對應key的null值。這樣大量的資料訪問會直接從redis獲取到null值,不會把壓力集中到資料庫層。(1,實際實現中,快取空值會設定較短的過期時間,一般1-3分鐘後就過期了。這樣設定的原因是:這些資料在redis中快取其實是沒有業務價值的,避免長期佔用空間;2,實際從快取獲取不到值後,在後面的實現中,一般會加鎖(分散式鎖或者本地鎖),具體看快取擊穿的解決辦法。)

3,採用布隆過濾器(Bloom Filter),基本思路就是開闢一塊單獨的空間存放所有可能的key值,在key值以內的,redis快取中獲取不到資料,才會查詢資料庫後再把結果寫入redis。

快取雪崩

問題產生

由於redis受限於記憶體空間,不能把所有的資料存放在快取中,快取的基本思想是存放高頻查詢的資料。所以,快取一般是會設定過期時間,過期後,快取資料會被清空。而每次快取啟動都會預熱資料,這樣,大量的資料可能會在同一時間被清空,而這個時候,如果這些資料存在大量的訪問,壓力會直接集中到資料庫上面,造成資料宕機。

解決方案

解決思路是:避免大量資料同時過期。可以在大批量資料同時快取的時候,快取的過期時間不採用固定值,採用隨機演算法,給指定的固定值增加一個隨機數時間長度。

快取擊穿

問題產生

熱點資料有大併發的請求量在請求快取的時候,由於快取過期,大量的請求瞬時訪問到資料,導致資料庫宕機。

解決方案

基本思路就是通過鎖的方式(快取擊穿的問題,本地鎖就可以解決問題。),程序中同時只允許一個執行緒去讀取資料庫並更新快取。

具體實現如下圖:

在這裡插入圖片描述

快取資料庫雙寫不一致

所謂雙寫不一致,就是快取和資料庫的資料不一致。站在業務的角度來分析,一般沒有要求快取、DB資料一直嚴格一致的情況,大多數情況是可以允許最終一致的。在 cache aside pattern 中推薦的寫模式,其實滿足絕大部分要求的。如果要儘可能的提高一致性,可以將同步的刪除快取做一個調整,採用非同步或者其他方式來重複嘗試確保刪除成,一般不建議佔用當前請求的時間。(網上有說阿里開源的資料同步方案:alibaba/canal 中雙一致方式是通過寫和訂閱資料庫的binlog 的方式去非同步實現刪除快取。有興趣的可以瞭解一下:https://github.com/alibaba/canal)

小結

本文簡單描述了redis快取在高併發場景下的一些常見問題。曾經見過有專案把redis純粹作為資料庫使用,而同時存在的mysql就像是一個另類的備份,這種模式下,對資料的讀寫和使用和作為快取使用區別很大,就不討論了。