快取穿透是指查詢乙個一定不存在的資料,由於快取是不命中時需要從資料庫查詢,查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,造成快取穿透。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。
解決方案:
1)有很多種方法可以有效地解決快取穿透問題,最常見的則是採用布隆過濾器,將所有可能存在的資料雜湊到乙個足夠大的bitmap中,乙個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層資料庫的查詢壓力。
2)另外也有乙個更為簡單粗暴的方法,如果乙個查詢返回的資料為空(不管是資料不存在,還是系統故障),仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。
快取雪崩是指在設定快取時採用了相同的過期時間,導致快取在某一時刻同時失效,導致所有的查詢都落在資料庫上,造成了快取雪崩。
解決方案:
1)在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許乙個執行緒查詢資料和寫快取,其他執行緒等待。
2)可以通過快取reload機制,預先去更新快取,在即將發生大併發訪問前手動觸發載入快取。
3)不同的key,設定不同的過期時間,讓快取失效的時間點盡量均勻。
4)做二級快取,或者雙快取策略。a1為原始快取,a2為拷貝快取,a1失效時,可以訪問a2,a1快取失效時間設定為短期,a2設定為長期。
對於一些設定了過期時間的key,如果這些key可能會在某些時間點被超高併發地訪問,是一種非常「熱點」的資料。這個時候,需要考慮乙個問題:快取被「擊穿」的問題,這個和快取雪崩的區別在於這裡針對某一key快取,前者則是很多key。
快取在某個時間點過期的時候,恰好在這個時間點對這個key有大量的併發請求過來,這些請求發現快取過期一般都會從後端db載入資料並回設到快取,這個時候大併發的請求可能會瞬間把後端db壓垮。
解決方案:
1)後台重新整理
後台定義乙個job(定時任務)專門主動更新快取資料.比如,乙個快取中的資料過期時間是30分鐘,那麼job每隔29分鐘定時重新整理資料(將從資料庫中查到的資料更新到快取中).
注:這種方案比較容易理解,但會增加系統複雜度。比較適合那些 key 相對固定,cache 粒度較大的業務,key 比較分散的則不太適合,實現起來也比較複雜。
2)檢查更新
將快取key的過期時間(絕對時間)一起儲存到快取中(可以拼接,可以新增新字段,可以採用單獨的key儲存..不管用什麼方式,只要兩者建立好關聯關係就行).在每次執行get操作後,都將get出來的快取過期時間與當前系統時間做乙個對比,如果快取過期時間-當前系統時間<=1分鐘(自定義的乙個值),則主動更新快取.這樣就能保證快取中的資料始終是最新的(和方案一一樣,讓資料不過期.)
注:這種方案在特殊情況下也會有問題。假設快取過期時間是12:00,而 11:59 到 12:00這 1 分鐘時間裡恰好沒有 get 請求過來,又恰好請求都在 11:30 分的時 候高併發過來,那就悲劇了。這種情況比較極端,但並不是沒有可能。因為「高 併發」也可能是階段性在某個時間點爆發。
3)分級快取
採用 l1 (一級快取)和 l2(二級快取) 快取方式,l1 快取失效時間短,l2 快取失效時間長。 請求優先從 l1 快取獲取資料,如果 l1快取未命中則加鎖,只有 1 個執行緒獲取到鎖,這個執行緒再從資料庫中讀取資料並將資料再更新到到 l1 快取和 l2 快取中,而其他執行緒依舊從 l2 快取獲取資料並返回。
注:這種方式,主要是通過避免快取同時失效並結合鎖機制實現。所以,當資料更 新時,只能淘汰 l1 快取,不能同時將 l1 和 l2 中的快取同時淘汰。l2 快取中 可能會存在髒資料,需要業務能夠容忍這種短時間的不一致。而且,這種方案 可能會造成額外的快取空間浪費。
4)加鎖
方法1:12
3456
78910
1112
13// 方法1:
public
synchronized listgetdata01()
return
result;
}
注:這種方式確實能夠防止快取失效時高併發到資料庫,但是快取沒有失效的時候,在從快取中拿資料時需要排隊取鎖,這必然會大大的降低了系統的吞吐量.
方法2:12
3456
78910
1112
1314
1516
17// 方法2:
static
object
lock
=
new
object();
public
listgetdata02()
}
return
result;
}
注:這個方法在快取命中的時候,系統的吞吐量不會受影響,但是當快取失效時,請求還是會打到資料庫,只不過不是高併發而是阻塞而已.但是,這樣會造成使用者體驗不佳,並且還給資料庫帶來額外壓力.
方法3:12
3456
78910
1112
1314
1516
1718
1920
//方法3
public
listgetdata03()
}
}
return
result;
}
注:雙重判斷雖然能夠阻止高併發請求打到資料庫,但是第二個以及之後的請求在命中快取時,還是排隊進行的.比如,當30個請求一起併發過來,在雙重判斷時,第乙個請求去資料庫查詢並更新快取資料,剩下的29個請求則是依次排隊取快取中取資料.請求排在後面的使用者的體驗會不爽.
方法4:12
3456
78910
1112
1314
1516
1718
1920
2122
2324
2526
2728
29static
lock reenlock =
new
reentrantlock();
public
listgetdata04() throws interruptedexception
finally
}
else
}
}
return
result;
}
注:最後使用互斥鎖的方式來實現,可以有效避免前面幾種問題.
當然,在實際分布式場景中,我們還可以使用 redis、tair、zookeeper 等提供的分布式鎖來實現.但是,如果我們的併發量如果只有幾千的話,何必殺雞焉用牛刀呢?
分布式 快取穿透 快取雪崩,快取擊穿解決方案
快取穿透是指查詢乙個一定不存在的資料,由於快取是不命中時需要從資料庫查詢,查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,造成快取穿透。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。解決方案 1 有很多種方法可以有效地解決快取穿透...
快取穿透 快取擊穿 快取雪崩
一 快取處理流程 前台請求,後台先從快取中取資料,取到直接返回結果,取不到時從資料庫中取,資料庫取到更新快取,並返回結果,資料庫也沒取到,那直接返回空結果。二 快取穿透 描述 快取穿透是指快取和資料庫中都沒有的資料,而使用者不斷發起請求,如發起為id為 1 的資料或id為特別大不存在的資料。這時的使...
快取穿透,快取擊穿,快取雪崩
所謂的快取穿透,簡單來講就是查詢某些不存在的key時,快取和資料庫查詢結果都為空,而空的結果又不被快取起來,而導致每次查詢都去請求資料庫層的情況。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。過程 快取不命中,進而導致每次查詢都去查詢資料庫,快取也就失去了作...