點擊上方「Java專欄」,選擇「置頂或者星標」
web
第一時間閱讀精彩文章!面試
來源:https://4m.cn/e3JwR算法
最近都沒看Redis,如今回來溫習下,如今從Redis的三大緩存開始從新探一探有多深有多淺(^▽^)
數據庫

讓我來開始知識的醍醐灌頂把!是時候表演真正的技術了。(嗶嗶嗶嗶....)數組

鐵子們,看在這麼賣力的份上,若是以爲本文對你有幫助的話,請動動你的小手,點個贊喲。緩存
接下來就開始咱們的Redis的三大緩存問題之旅,讓咱們坐上小飛船遊一遊這聖女峯。微信

在Redis緩存中有三個必需要知道概念:緩存穿透、緩存擊穿和緩存雪崩。數據結構
緩存穿透
那什麼是緩存穿透,它就是指當用戶在查詢一條數據的時候,而此時數據庫和緩存卻沒有關於這條數據的任何記錄,而這條數據在緩存中沒找到就會向數據庫請求獲取數據。它拿不到數據時,是會一直查詢數據庫,這樣會對數據庫的訪問形成很大的壓力。併發
如:用戶查詢一個 id = -1 的商品信息,通常數據庫 id 值都是從 1 開始自增,很明顯這條信息是不在數據庫中,當沒有信息返回時,會一直向數據庫查詢,給當前數據庫的形成很大的訪問壓力。
這時候咱們要想想,該如何解決這個問題呢?o(╥﹏╥)o
通常咱們能夠想到從緩存開始出發,想若是咱們給緩存設置一個若是當前數據庫不存在的信息,把它緩存成一個空對象,返回給用戶。
^_^沒錯,這是一個解決方案,也就是咱們常說的緩存空對象(代碼維護簡單,可是效果不是很好)。
Redis 也爲咱們提供了一種解決方案,那就是布隆過濾器(代碼維護比較複雜,效果挺好的)。
搜索Java專欄公衆號,回覆「手冊」,送你一份Java面試題寶典.pdf
那接下來,先解釋下這兩種方案:
緩存空對象
那什麼是緩存空對象呀!別急,緩存空對象它就是指一個請求發送過來,若是此時緩存中和數據庫都不存在這個請求所要查詢的相關信息,那麼數據庫就會返回一個空對象,並將這個空對象和請求關聯起來存到緩存中,當下次仍是這個請求過來的時候,這時緩存就會命中,就直接從緩存中返回這個空對象,這樣能夠減小訪問數據庫的壓力,提升當前數據庫的訪問性能。咱們接下來能夠看下面這個流程呀~

這時候,咱們就會問了呀 ~,若是大量不存在的請求過來,那麼這時候緩存豈不是會緩存許多空對象了嗎~~~
沒錯哦!這也是使用緩存空對象會致使的一個問題:若是時間一長這樣會致使緩存中存在大量空對象,這樣不只會佔用許多的內存空間,還會浪費許多資源呀!。那這有沒有什麼能夠解決的方法呢?咱們來想想:咱們能夠將這些對象在一段時間以後清理下不久能夠了嗎 ~
嗯嗯,沒錯!在想一想 Redis 裏是否是給咱們提供了有關過時時間的命令呀(^▽^),這樣咱們能夠在設置空對象的時間,順便設置一個過時時間,就能夠解決個問題了呀!
setex key seconds valule:設置鍵值對的同時指定過時時間(s)
在Java 中直接調用 API 操做便可:
redisCache.put(Integer.toString(id), null, 60) //過時時間爲 60s
布隆過濾器
那布隆過濾器是否是不是一個過濾器,過濾東西的呀!哎呀,你太聰明瞭,沒錯它就是用來過濾東西的,它是一種基於機率的數據結構,主要使用愛判斷當前某個元素是否在該集合中,運行速度快。咱們也能夠簡單理解爲是一個不怎麼精確的 set 結構(set 具備去重的效果)。可是有個小問題是:當你使用它的 contains 方法去判斷某個對象是否存在時,它可能會誤判。也就是說布隆過濾器不是特別不精確,可是隻要參數設置的合理,它的精確度能夠控制的相對足夠精確,只會有小小的誤判機率(這是能夠接受的呀 ~)。當布隆過濾器說某個值存在時,這個值可能不存在;當它說不存在時,那就確定不存在。
這裏有個典型的例子呀,來自錢大:
打個比方,當它說不認識你時,確定就不認識;當它說見過你時,可能根本就沒見過面,不過由於你的臉跟它認識的人中某臉比較類似 (某些熟臉的係數組合),因此誤判之前見過你。在上面的使用場景中,布隆過濾器能準確過濾掉那些已經看過的內容,那些沒有看過的新內容,它也會過濾掉極小一部分 (誤判),可是絕大多數新內容它都能準確識別。這樣就能夠徹底保證推薦給用戶的內容都是無重複的。
說了這麼久,那布隆過濾器到底有什麼特色呢:
特色嗎,多多來讓一個個跟你吹吹(吹到你懷疑人生(≧∇≦)ノ)
-
一個很是大的二進制位數組(數組中只存在 0 和 1)
-
擁有若干個哈希函數(Hash Function)
-
在空間效率和查詢效率都很是高
-
布隆過濾器不會提供刪除方法,在代碼維護上比較困難。
每一個布隆過濾器對應到 Redis 的數據結構裏面就是一個大型的位數組和幾個不同的無偏 hash 函數。所謂無偏就是可以把元素的 hash 值算得比較均勻。

向布隆過濾器中添加 key 時,會使用多個 hash 函數對 key 進行 hash 算得一個整數索引值而後對位數組長度進行取模運算獲得一個位置,每一個 hash 函數都會算得一個不一樣的位置。再把位數組的這幾個位置都置爲 1 就完成了 add 操做。( 每個 key 都經過若干的hash函數映射到一個巨大位數組上,映射成功後,會在把位數組上對應的位置改成1。)
那爲何布隆過濾器會存在誤判率呢?
誤判嗎?人生哪有不摔跤,只要鋤頭揮得好,照樣能挖到。(咳咳咳,說偏了...)
其實它會誤判是以下這個狀況:

當 key1 和 key2 映射到位數組上的位置爲 1 時,假設這時候來了個 key3,要查詢是否是在裏面,剛好 key3 對應位置也映射到了這之間,那麼布隆過濾器會認爲它是存在的,這時候就會產生誤判(由於明明 key3 是不在的)。
O(∩_∩)O哈哈~,這時候你會問了:如何提升布隆過濾器的準確率呢?
要提升布隆過濾器的準確率,就要說到影響它的三個重要因素:
-
哈希函數的好壞
-
存儲空間大小
-
哈希函數個數
hash函數的設計也是一個十分重要的問題,對於好的hash函數能大大下降布隆過濾器的誤判率。
(這就比如優秀的配件之因此可以運行這麼順暢就在於其內部設計的得當。)
同時,對於一個布隆過濾器來講,若是其位數組越大的話,那麼每一個key經過hash函數映射的位置會變得稀疏許多,不會那麼緊湊,有利於提升布隆過濾器的準確率。同時,對於一個布隆過濾器來講,若是key經過許多hash函數映射,那麼在位數組上就會有許多位置有標誌,這樣當用戶查詢的時候,在經過布隆過濾器來找的時候,誤判率也會相應下降。
對於其內部原理,有興趣的同窗能夠看看關於布隆過濾的數學知識,裏面有關於它的設計算法和數學知識。(其實也挺簡單~)
緩存擊穿
緩存擊穿是指有某個key常常被查詢,常常被用戶特殊關懷,用戶很是 love 它 (^▽^),也就類比「熟客」 或者 一個key常常不被訪問。可是這時候,若是這個key在緩存的過時時間失效的時候或者這是個冷門key時,這時候忽然有大量有關這個key的訪問請求,這樣會致使大併發請求直接穿透緩存,請求數據庫,瞬間對數據庫的訪問壓力增大。
概括起來:形成緩存擊穿的緣由有兩個。
(1)一個「冷門」key,忽然被大量用戶請求訪問。
(2)一個「熱門」key,在緩存中時間剛好過時,這時有大量用戶來進行訪問。

對於緩存擊穿的問題:咱們經常使用的解決方案是加鎖。對於key過時的時候,當key要查詢數據庫的時候加上一把鎖,這時只能讓第一個請求進行查詢數據庫,而後把從數據庫中查詢到的值存儲到緩存中,對於剩下的相同的key,能夠直接從緩存中獲取便可。
若是咱們是在單機環境下:直接使用經常使用的鎖便可(如:Lock、Synchronized等),在分佈式環境下咱們可使用分佈式鎖,如:基於數據庫、基於Redis或者zookeeper 的分佈式鎖。

緩存雪崩
緩存雪崩是指在某一個時間段內,緩存集中過時失效,若是這個時間段內有大量請求,而查詢數據量巨大,全部的請求都會達到存儲層,存儲層的調用量會暴增,引發數據庫壓力過大甚至宕機。
搜索Java專欄公衆號,回覆「手冊」,送你一份Java面試題寶典.pdf
緣由:
-
Redis忽然宕機
-
大部分數據失效
舉個例子理解下吧:
好比咱們基本上都經歷過購物狂歡節,假設商家舉辦 23:00-24:00 商品打骨折促銷活動。程序小哥哥在設計的時候,在 23:00 把商家打骨折的商品放到緩存中,並經過redis的expire設置了過時時間爲1小時。這個時間段許多用戶訪問這些商品信息、購買等等。可是恰好到了24:00點的時候,剛好還有許多用戶在訪問這些商品,這時候對這些商品的訪問都會落到數據庫上,致使數據庫要抗住巨大的壓力,稍有不慎會致使,數據庫直接宕機(over)。
當商品沒有失效的時候是這樣的:

當緩存GG(失效)的時候倒是這樣的:

對於緩存雪崩有如下解決方案:
(1)redis高可用
redis有可能掛掉,多增長几臺redis實例,(一主多從或者多主多從),這樣一臺掛掉以後其餘的還能夠繼續工做,其實就是搭建的集羣。
(2)限流降級
在緩存失效後,經過加鎖或者隊列來控制讀數據庫寫緩存的線程數量,對某個key只容許一個線程查詢數據和寫緩存,其餘線程等待。
(3)數據預熱
數據加熱的含義就是在正式部署以前,我先把可能的數據先預先訪問一遍,這樣部分可能大量訪問的數據就會加載到緩存中。在即將發生大併發訪問前手動觸發加載緩存不一樣的key。
(4)不一樣的過時時間
設置不一樣的過時時間,讓緩存失效的時間點儘可能均勻。
150頁,8W字的Java知識手冊
獲取方式:
一、公衆號後臺回覆「手冊」
二、掃描下方二維碼,回覆「手冊」
👆 長按上方二維碼 2 秒 回覆「 手冊 」便可獲取資料
本文分享自微信公衆號 - Java專欄(finishbug)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。