Redis缓存击穿和缓存雪崩、缓存穿透以及对应的解决方案
目錄
緩存擊穿
緩存擊穿的解決方案
緩存雪崩
緩存雪崩的解決方案
緩存穿透
布隆過濾器
緩存擊穿
一般我們會對緩存的key設置過期時間,在高并發下,如果在某一時刻這個key剛好過期,此時持續的大并發請求都會穿破緩存,直接命中DB,就像在一個屏障上鑿開了一個洞。
緩存擊穿的解決方案
1)? 通過分布式鎖或者隊列,使得同一個key只允許一個線程到數據庫查詢
2)定時預先更新緩存,避免發生緩存失效的情形
不過一般情況下單個緩存的擊穿很難對數據庫造成壓垮性的壓力
緩存雪崩
緩存雪崩是指緩存中的大量的key在同一時間失效,導致所有的請求都命中DB,使得其無法負載。簡單來說就是大量key同時發生緩存擊穿的情形
緩存擊穿針對的是某一個設置了過期時間的緩存,緩存雪崩針對的是很多設置了過期時間的緩存
緩存雪崩的解決方案
1)? 通過分布式鎖或者隊列,使得同一個key只允許一個線程到數據庫查詢
2)定時預先更新緩存,避免同時發生緩存失效的情形
3)通過加隨機數,使得不同的key在不同的時間過期
4)設置緩存永不過期
緩存穿透
緩存穿透,是指查詢一個數據庫一定不存在的數據。通常redis沒有緩存的時候,我們就會去訪問數據庫獲取數據,此時如果數據庫也不存在數據,那么每次對該key的請求都會命中數據庫,在高并發情形下,就會導致數據庫壓力過大從而崩潰。
如果每次請求的數據都是同一個key,那么可以通過緩存空數據或者特殊字符串,比如&&來避免緩存穿透(需要對這個緩存設置一個過期時間,否則數據庫新增了這一數據也無法獲取到對應值)
但是如果每次請求的key都不一樣,那么我們就需要其他的方法來判斷一個key是否存在于數據庫中,不能直接通過查詢數據庫的方式來判斷
因為緩存的往往是大量的數據,直接存儲所有的數據顯然費空間又費時間,為了節省存儲空間,可以采用BitMap(位圖)的方式來標記這個key是否出現過
對于每一個元素,我們通過哈希函數對key進行運算,得到一個下標,將其對應的位圖上的值改為1,就表示該元素在這個集合存在。因為哈希碰撞的存在,我們可以采用
1.擴大數組的長度或者說位圖容量 (空間消耗加大)
2.引入很多個哈希函數 (耗時)
來減小哈希碰撞的概率
實際使用中,我們既要節省空間,又要很高的計算效率,就必須在位圖容量和函數個數之間找到一個最優解,布隆過濾器就幫助我們解決了這個問題
布隆過濾器
1970年由Burton Howard Bloom提出,其目的就是為了快速判斷一個集合里是否存在某一個元素
bloom過濾器實際是由一個長位圖和k個不同的哈希函數組成的
其過程主要分為三步:
1.創建一個二進制數組,所有的值都初始化為0
2.將集合里的每一個元素都通過k個hash函數運算其在位圖的下標
3.將對應的下標的值改為1
判斷某個元素是否存在于集合的時候,我們只需要判斷元素通過k個hash函數計算出來的下標在位圖上是否都為1就可以了,如果全部為1,則表示該元素很可能存在;否則該元素就一定不存在(這是因為hash碰撞無法避免,會導致有一定的誤判率,會將不存在的元素誤判為存在)
這種把本來不存在布隆過濾器中的元素誤判為存在的情況,我們把它叫做假陽性(False Positive Probability,FPP)。
Funnel<String> funnel = new Funnel<String>() {@Overridepublic void funnel(String from, PrimitiveSink into) {into.putString(from, Charsets.UTF_8);}};// 創建符合條件的布隆過濾器 誤判率為0.1%,集合元素最大預計為一萬BloomFilter<String> filter = BloomFilter.create(funnel, 10000, 0.001);filter.put("chenpp");System.out.println(filter.mightContain("chenpp"));System.out.println(filter.mightContain("chenpp11"));如果在緩存中使用布隆過濾器,其流程圖如下:
首先每次在數據庫新增數據的時候,都需要把數據同步到BloomFilter中
在查詢數據的時候,先在布隆過濾器查詢,如果返回說沒有,那數據庫中也肯定沒有,就不需要查了,只有返回為存在的數據才需要查詢緩存和DB
?
總結
以上是生活随笔為你收集整理的Redis缓存击穿和缓存雪崩、缓存穿透以及对应的解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Maven archetype 自定
- 下一篇: spring源码构建以及模块划分和依赖