緩存穿透和緩存雪崩


最近發現數據庫的QPS定期飆高,簡單排查后,定位到原因是由於定期執行的任務,會對數據庫有大量的訪問。但奇怪的是,有的數據,我明明做了緩存,但是依然對數據庫的請求量很大。

 

原因是,當緩存里沒有我查詢的數據,數據庫里也沒有,這時每次都會去查數據庫。打個比方,你把某個DO做了緩存,key是主鍵,value是DO。如果你拿一個不存在於數據庫里的key,去查詢數據,會發生什么?

根據我在緩存與數據庫的一致性思考中介紹的,首先會發現緩存里沒有數據,然后取查數據庫。數據庫里雖然沒有數據,但是我會把null放進緩存里。下次查詢的時候,緩存依然沒有數據,結果繼續查數據庫。周而復始,每次都在查數據庫。

所以你會發現,問題的核心在於,不應該把null放進緩存里。而是應該放入一個特殊的對象。這樣下次查詢的時候,當我從緩存里查到這個特殊的對象,我就會知道,數據庫里是沒有值的,我不用去撈數據庫了,直接返回個null出去。

以上就是所謂的緩存穿透。

緩存穿透的解決方案,除了我上面所說的之外,還有一種。就是把數據庫里沒有的key,維護在一個列表里。每次查詢之前,先看看key在不在列表里。如果在,則也不用去查數據庫了。

緩存穿透會帶來什么后果呢?一是數據庫的QPS會很高,而你根本不知道為什么,因為你覺得自己已經做了緩存了。二是如果別人發現你這個問題,就可以借此來攻擊你,讓你的數據庫掛掉。

 

接着,我們來談談緩存雪崩。緩存雪崩的意思是,當你的緩存,在某一個時刻,發生大規模失效。例如你所有的緩存是同時加載的,而且失效時間設置的一樣長。那么在緩存失效的那一刻,如果有大量的查詢請求進來,這些請求都會直接打在數據庫上,結果就是數據庫扛不住壓力,掛了。

解決的方案是:

1. 不要讓緩存在某一時刻大面積失效。要么,改變緩存的失效時間,不過對於同一個緩存,這個貌似不可能(不可能我這個DO的失效時間短,那個DO的失效時間長),除非你搞兩個緩存;要么,加載緩存的時間點弄得分散一些,不要同時加載緩存。

2. 不要讓所有相同的請求,都打在數據庫上。例如,有很多線程,都在請求某個相同的key,那么,讓這些線程去搶着鎖住key。鎖住key的線程,去訪問數據庫,並更新緩存。沒搶到的,則等着。這個方案的問題是,在沒有雪崩的時候,你也得去搶鎖,那效率就呵呵了。我感覺這個方案不怎么現實。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM