成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

爛大街的緩存穿透、緩存擊穿和緩存雪崩,你真的懂了?

開發(fā) 后端
對于從事后端開發(fā)的同學(xué)來說,緩存已經(jīng)變成的項(xiàng)目中必不可少的技術(shù)之一。今天我們一起聊聊如果在項(xiàng)目中引入了緩存,可能會給我們帶來的下面這三大問題。看看你中招了沒?

[[441985]]

前言

對于從事后端開發(fā)的同學(xué)來說,緩存已經(jīng)變成的項(xiàng)目中必不可少的技術(shù)之一。

沒錯(cuò),緩存能給我們系統(tǒng)顯著的提升性能。但如果你使用不好,或者缺乏相關(guān)經(jīng)驗(yàn),它也會帶來很多意想不到的問題。

今天我們一起聊聊如果在項(xiàng)目中引入了緩存,可能會給我們帶來的下面這三大問題。看看你中招了沒?

1. 緩存穿透問題

大部分情況下,加緩存的目的是:為了減輕數(shù)據(jù)庫的壓力,提升系統(tǒng)的性能。

1.1 我們是如何用緩存的?

一般情況下,如果有用戶請求過來,先查緩存,如果緩存中存在數(shù)據(jù),則直接返回。如果緩存中不存在,則再查數(shù)據(jù)庫,如果數(shù)據(jù)庫中存在,則將數(shù)據(jù)放入緩存,然后返回。如果數(shù)據(jù)庫中也不存在,則直接返回失敗。

流程圖如下:

上面的這張圖小伙們肯定再熟悉不過了,因?yàn)榇蟛糠志彺娑际沁@樣用的。

1.2 什么是緩存穿透?

但如果出現(xiàn)以下這兩種特殊情況,比如:

用戶請求的id在緩存中不存在。

惡意用戶偽造不存在的id發(fā)起請求。

這樣的用戶請求導(dǎo)致的結(jié)果是:每次從緩存中都查不到數(shù)據(jù),而需要查詢數(shù)據(jù)庫,同時(shí)數(shù)據(jù)庫中也沒有查到該數(shù)據(jù),也沒法放入緩存。也就是說,每次這個(gè)用戶請求過來的時(shí)候,都要查詢一次數(shù)據(jù)庫。

圖中標(biāo)紅的箭頭表示每次走的路線。

很顯然,緩存根本沒起作用,好像被穿透了一樣,每次都會去訪問數(shù)據(jù)庫。

這就是我們所說的:緩存穿透問題。

如果此時(shí)穿透了緩存,而直接數(shù)據(jù)庫的請求數(shù)量非常多,數(shù)據(jù)庫可能因?yàn)榭覆蛔毫Χ鴴斓簟鑶鑶琛?/p>

那么問題來了,如何解決這個(gè)問題呢?

1.3 校驗(yàn)參數(shù)

我們可以對用戶id做檢驗(yàn)。

比如你的合法id是15xxxxxx,以15開頭的。如果用戶傳入了16開頭的id,比如:16232323,則參數(shù)校驗(yàn)失敗,直接把相關(guān)請求攔截掉。這樣可以過濾掉一部分惡意偽造的用戶id。

1.4 布隆過濾器

如果數(shù)據(jù)比較少,我們可以把數(shù)據(jù)庫中的數(shù)據(jù),全部放到內(nèi)存的一個(gè)map中。

這樣能夠非常快速的識別,數(shù)據(jù)在緩存中是否存在。如果存在,則讓其訪問緩存。如果不存在,則直接拒絕該請求。

但如果數(shù)據(jù)量太多了,有數(shù)千萬或者上億的數(shù)據(jù),全都放到內(nèi)存中,很顯然會占用太多的內(nèi)存空間。

那么,有沒有辦法減少內(nèi)存空間呢?

答:這就需要使用布隆過濾器了。

布隆過濾器底層使用bit數(shù)組存儲數(shù)據(jù),該數(shù)組中的元素默認(rèn)值是0。

布隆過濾器第一次初始化的時(shí)候,會把數(shù)據(jù)庫中所有已存在的key,經(jīng)過一些列的hash算法(比如:三次hash算法)計(jì)算,每個(gè)key都會計(jì)算出多個(gè)位置,然后把這些位置上的元素值設(shè)置成1。

之后,有用戶key請求過來的時(shí)候,再用相同的hash算法計(jì)算位置。

  • 如果多個(gè)位置中的元素值都是1,則說明該key在數(shù)據(jù)庫中已存在。這時(shí)允許繼續(xù)往后面操作。
  • 如果有1個(gè)以上的位置上的元素值是0,則說明該key在數(shù)據(jù)庫中不存在。這時(shí)可以拒絕該請求,而直接返回。

使用布隆過濾器確實(shí)可以解決緩存穿透問題,但同時(shí)也帶來了兩個(gè)問題:

  1. 存在誤判的情況。
  2. 存在數(shù)據(jù)更新問題。

先看看為什么會存在誤判呢?

上面我已經(jīng)說過,初始化數(shù)據(jù)時(shí),針對每個(gè)key都是通過多次hash算法,計(jì)算出一些位置,然后把這些位置上的元素值設(shè)置成1。

但我們都知道hash算法是會出現(xiàn)hash沖突的,也就是說不通的key,可能會計(jì)算出相同的位置。

上圖中的下標(biāo)為2的位置就出現(xiàn)了hash沖突,key1和key2計(jì)算出了一個(gè)相同的位置。

如果有幾千萬或者上億的數(shù)據(jù),布隆過濾器中的hash沖突會非常明顯。

如果某個(gè)用戶key,經(jīng)過多次hash計(jì)算出的位置,其元素值,恰好都被其他的key初始化成了1。此時(shí),就出現(xiàn)了誤判,原本這個(gè)key在數(shù)據(jù)庫中是不存在的,但布隆過濾器確認(rèn)為存在。

如果布隆過濾器判斷出某個(gè)key存在,可能出現(xiàn)誤判。如果判斷某個(gè)key不存在,則它在數(shù)據(jù)庫中一定不存在。

通常情況下,布隆過濾器的誤判率還是比較少的。即使有少部分誤判的請求,直接訪問了數(shù)據(jù)庫,但如果訪問量并不大,對數(shù)據(jù)庫影響也不大。

此外,如果想減少誤判率,可以適當(dāng)增加hash函數(shù),圖中用的3次hash,可以增加到5次。

其實(shí),布隆過濾器最致命的問題是:如果數(shù)據(jù)庫中的數(shù)據(jù)更新了,需要同步更新布隆過濾器。但它跟數(shù)據(jù)庫是兩個(gè)數(shù)據(jù)源,就可能存在數(shù)據(jù)不一致的情況。

比如:數(shù)據(jù)庫中新增了一個(gè)用戶,該用戶數(shù)據(jù)需要實(shí)時(shí)同步到布隆過濾。但由于網(wǎng)絡(luò)異常,同步失敗了。

這時(shí)剛好該用戶請求過來了,由于布隆過濾器沒有該key的數(shù)據(jù),所以直接拒絕了該請求。但這個(gè)是正常的用戶,也被攔截了。

很顯然,如果出現(xiàn)了這種正常用戶被攔截了情況,有些業(yè)務(wù)是無法容忍的。所以,布隆過濾器要看實(shí)際業(yè)務(wù)場景再決定是否使用,它幫我們解決了緩存穿透問題,但同時(shí)了帶來了新的問題。

1.5 緩存空值

上面使用布隆過濾器,雖說可以過濾掉很多不存在的用戶id請求。但它除了增加系統(tǒng)的復(fù)雜度之外,會帶來兩個(gè)問題:

布隆過濾器存在誤殺的情況,可能會把少部分正常用戶的請求也過濾了。

如果用戶信息有變化,需要實(shí)時(shí)同步到布隆過濾器,不然會有問題。

所以,通常情況下,我們很少用布隆過濾器解決緩存穿透問題。其實(shí),還有另外一種更簡單的方案,即:緩存空值。

當(dāng)某個(gè)用戶id在緩存中查不到,在數(shù)據(jù)庫中也查不到時(shí),也需要將該用戶id緩存起來,只不過值是空的。這樣后面的請求,再拿相同的用戶id發(fā)起請求時(shí),就能從緩存中獲取空數(shù)據(jù),直接返回了,而無需再去查一次數(shù)據(jù)庫。

優(yōu)化之后的流程圖如下:

關(guān)鍵點(diǎn)是不管從數(shù)據(jù)庫有沒有查到數(shù)據(jù),都將結(jié)果放入緩存中,只是如果沒有查到數(shù)據(jù),緩存中的值是空的罷了。

2. 緩存擊穿問題

2.1 什么是緩存擊穿?

有時(shí)候,我們在訪問熱點(diǎn)數(shù)據(jù)時(shí)。比如:我們在某個(gè)商城購買某個(gè)熱門商品。

為了保證訪問速度,通常情況下,商城系統(tǒng)會把商品信息放到緩存中。但如果某個(gè)時(shí)刻,該商品到了過期時(shí)間失效了。

此時(shí),如果有大量的用戶請求同一個(gè)商品,但該商品在緩存中失效了,一下子這些用戶請求都直接懟到數(shù)據(jù)庫,可能會造成瞬間數(shù)據(jù)庫壓力過大,而直接掛掉。

流程圖如下:

那么,如何解決這個(gè)問題呢?

2.2 加鎖

數(shù)據(jù)庫壓力過大的根源是,因?yàn)橥粫r(shí)刻太多的請求訪問了數(shù)據(jù)庫。

如果我們能夠限制,同一時(shí)刻只有一個(gè)請求才能訪問某個(gè)productId的數(shù)據(jù)庫商品信息,不就能解決問題了?

答:沒錯(cuò),我們可以用加鎖的方式,實(shí)現(xiàn)上面的功能。

偽代碼如下:

  1. try { 
  2.   String result = jedis.set(productId, requestId, "NX""PX", expireTime); 
  3.   if ("OK".equals(result)) { 
  4.     return queryProductFromDbById(productId); 
  5.   } 
  6. } finally{ 
  7.     unlock(productId,requestId); 
  8. }   
  9. return null

在訪問數(shù)據(jù)庫時(shí)加鎖,防止多個(gè)相同productId的請求同時(shí)訪問數(shù)據(jù)庫。

然后,還需要一段代碼,把從數(shù)據(jù)庫中查詢到的結(jié)果,又重新放入緩存中。辦法挺多的,在這里我就不展開了。

2.3 自動續(xù)期

出現(xiàn)緩存擊穿問題是由于key過期了導(dǎo)致的。那么,我們換一種思路,在key快要過期之前,就自動給它續(xù)期,不就OK了?

答:沒錯(cuò),我們可以用job給指定key自動續(xù)期。

比如說,我們有個(gè)分類功能,設(shè)置的緩存過期時(shí)間是30分鐘。但有個(gè)job每隔20分鐘執(zhí)行一次,自動更新緩存,重新設(shè)置過期時(shí)間為30分鐘。

這樣就能保證,分類緩存不會失效。

此外,在很多請求第三方平臺接口時(shí),我們往往需要先調(diào)用一個(gè)獲取token的接口,然后用這個(gè)token作為參數(shù),請求真正的業(yè)務(wù)接口。一般獲取到的token是有有效期的,比如24小時(shí)之后失效。

如果我們每次請求對方的業(yè)務(wù)接口,都要先調(diào)用一次獲取token接口,顯然比較麻煩,而且性能不太好。

這時(shí)候,我們可以把第一次獲取到的token緩存起來,請求對方業(yè)務(wù)接口時(shí)從緩存中獲取token。

同時(shí),有一個(gè)job每隔一段時(shí)間,比如每隔12個(gè)小時(shí)請求一次獲取token接口,不停刷新token,重新設(shè)置token的過期時(shí)間。

2.4 緩存不失效

此外,對于很多熱門key,其實(shí)是可以不用設(shè)置過期時(shí)間,讓其永久有效的。

比如參與秒殺活動的熱門商品,由于這類商品id并不多,在緩存中我們可以不設(shè)置過期時(shí)間。

在秒殺活動開始前,我們先用一個(gè)程序提前從數(shù)據(jù)庫中查詢出商品的數(shù)據(jù),然后同步到緩存中,提前做預(yù)熱。

等秒殺活動結(jié)束一段時(shí)間之后,我們再手動刪除這些無用的緩存即可。

3. 緩存雪崩問題

3.1 什么是緩存雪崩?

前面已經(jīng)聊過緩存擊穿問題了。

而緩存雪崩是緩存擊穿的升級版,緩存擊穿說的是某一個(gè)熱門key失效了,而緩存雪崩說的是有多個(gè)熱門key同時(shí)失效。看起來,如果發(fā)生緩存雪崩,問題更嚴(yán)重。

緩存雪崩目前有兩種:

  1. 有大量的熱門緩存,同時(shí)失效。會導(dǎo)致大量的請求,訪問數(shù)據(jù)庫。而數(shù)據(jù)庫很有可能因?yàn)榭覆蛔毫Γ苯訏斓簟?/li>
  2. 緩存服務(wù)器down機(jī)了,可能是機(jī)器硬件問題,或者機(jī)房網(wǎng)絡(luò)問題。總之,造成了整個(gè)緩存的不可用。

歸根結(jié)底都是有大量的請求,透過緩存,而直接訪問數(shù)據(jù)庫了。

那么,要如何解決這個(gè)問題呢?

3.2 過期時(shí)間加隨機(jī)數(shù)

為了解決緩存雪崩問題,我們首先要盡量避免緩存同時(shí)失效的情況發(fā)生。

這就要求我們不要設(shè)置相同的過期時(shí)間。

可以在設(shè)置的過期時(shí)間基礎(chǔ)上,再加個(gè)1~60秒的隨機(jī)數(shù)。

  1. 實(shí)際過期時(shí)間 = 過期時(shí)間 + 1~60秒的隨機(jī)數(shù) 

這樣即使在高并發(fā)的情況下,多個(gè)請求同時(shí)設(shè)置過期時(shí)間,由于有隨機(jī)數(shù)的存在,也不會出現(xiàn)太多相同的過期key。

3.3 高可用

針對緩存服務(wù)器down機(jī)的情況,在前期做系統(tǒng)設(shè)計(jì)時(shí),可以做一些高可用架構(gòu)。

比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出現(xiàn)單節(jié)點(diǎn)故障導(dǎo)致整個(gè)redis服務(wù)不可用的情況。

使用哨兵模式之后,當(dāng)某個(gè)master服務(wù)下線時(shí),自動將該master下的某個(gè)slave服務(wù)升級為master服務(wù),替代已下線的master服務(wù)繼續(xù)處理請求。

3.4 服務(wù)降級

如果做了高可用架構(gòu),redis服務(wù)還是掛了,該怎么辦呢?

這時(shí)候,就需要做服務(wù)降級了。

我們需要配置一些默認(rèn)的兜底數(shù)據(jù)。

程序中有個(gè)全局開關(guān),比如有10個(gè)請求在最近一分鐘內(nèi),從redis中獲取數(shù)據(jù)失敗,則全局開關(guān)打開。后面的新請求,就直接從配置中心中獲取默認(rèn)的數(shù)據(jù)。

當(dāng)然,還需要有個(gè)job,每隔一定時(shí)間去從redis中獲取數(shù)據(jù),如果在最近一分鐘內(nèi)可以獲取到兩次數(shù)據(jù)(這個(gè)參數(shù)可以自己定),則把全局開關(guān)關(guān)閉。后面來的請求,又可以正常從redis中獲取數(shù)據(jù)了。

需要特別說一句,該方案并非所有的場景都適用,需要根據(jù)實(shí)際業(yè)務(wù)場景決定。

 

責(zé)任編輯:姜華 來源: 蘇三說技術(shù)
相關(guān)推薦

2019-11-05 14:24:31

緩存雪崩框架

2019-10-12 14:19:05

Redis數(shù)據(jù)庫緩存

2023-03-10 13:33:00

緩存穿透緩存擊穿緩存雪崩

2021-06-05 09:01:01

Redis緩存雪崩緩存穿透

2020-03-16 14:57:24

Redis面試雪崩

2024-03-12 10:44:42

2022-05-27 07:57:20

緩存穿透緩存雪崩緩存擊穿

2022-03-08 00:07:51

緩存雪崩數(shù)據(jù)庫

2022-11-18 14:34:28

2023-11-10 14:58:03

2023-04-14 07:34:19

2025-06-30 01:55:00

2022-07-11 07:36:36

緩存緩存雪崩緩存擊穿

2020-10-13 07:44:40

緩存雪崩 穿透

2023-12-06 13:38:00

Redis緩存穿透緩存擊穿

2020-03-05 09:09:18

緩存原因方案

2020-10-23 10:46:03

緩存雪崩擊穿

2020-12-28 12:37:36

緩存擊穿穿透

2024-04-18 11:43:28

緩存數(shù)據(jù)庫Redis

2024-04-07 00:00:02

Redis雪崩緩存
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 免费永久av | 国产在线一区二区三区 | 欧美一级视频免费看 | 午夜精品久久久久久久星辰影院 | 一二三四在线视频观看社区 | 尤物在线视频 | 欧美三级久久久 | av免费看片| 精品一区二区三区四区视频 | 久久艹av| 中文字幕乱码一区二区三区 | 国产精品久久久久久久白浊 | 久日精品 | 欧美精品区 | 国产色 | 成年女人免费v片 | 欧美成视频 | 日韩精品一区二区三区在线播放 | 国产在线小视频 | 亚洲人精品午夜 | 国产精品国产精品 | 国产亚洲精品久久19p | 亚洲免费人成在线视频观看 | 一区欧美 | 网站黄色在线免费观看 | 美女一级毛片 | 男女免费网站 | 九九热精品视频 | 深夜福利影院 | 玖玖综合网| 国产精品乱码一区二区三区 | 亚洲免费在线观看 | 超碰8| 夜夜摸天天操 | 欧美一级二级视频 | 欧美日韩综合 | 久久之精品 | 亚洲精品电影网在线观看 | 久久av一区二区三区 | 国产一区二区免费电影 | 毛片韩国 |