最近發現資料庫的qps定期飆高,簡單排查後,定位到原因是由於定期執行的任務,會對資料庫有大量的訪問。但奇怪的是,有的資料,我明明做了快取,但是依然對資料庫的請求量很大。
原因是,當快取裡沒有我查詢的資料,資料庫裡也沒有,這時每次都會去查資料庫。打個比方,你把某個do做了快取,key是主鍵,value是do。如果你拿乙個不存在於資料庫裡的key,去查詢資料,會發生什麼?
根據我在快取與資料庫的一致性思考中介紹的,首先會發現快取裡沒有資料,然後取查資料庫。資料庫裡雖然沒有資料,但是我會把null放進快取裡。下次查詢的時候,快取依然沒有資料,結果繼續查資料庫。周而復始,每次都在查資料庫。
所以你會發現,問題的核心在於,不應該把null放進快取裡。而是應該放入乙個特殊的物件。這樣下次查詢的時候,當我從快取里查到這個特殊的物件,我就會知道,資料庫裡是沒有值的,我不用去撈資料庫了,直接返回個null出去。
以上就是所謂的快取穿透。
快取穿透的解決方案,除了我上面所說的之外,還有一種。就是把資料庫裡沒有的key,維護在乙個列表裡。每次查詢之前,先看看key在不在列表裡。如果在,則也不用去查資料庫了。
快取穿透會帶來什麼後果呢?一是資料庫的qps會很高,而你根本不知道為什麼,因為你覺得自己已經做了快取了。二是如果別人發現你這個問題,就可以藉此來攻擊你,讓你的資料庫掛掉。
接著,我們來談談快取雪崩。快取雪崩的意思是,當你的快取,在某乙個時刻,發生大規模失效。例如你所有的快取是同時載入的,而且失效時間設定的一樣長。那麼在快取失效的那一刻,如果有大量的查詢請求進來,這些請求都會直接打在資料庫上,結果就是資料庫扛不住壓力,掛了。
解決的方案是:
1. 不要讓快取在某一時刻大面積失效。要麼,改變快取的失效時間,不過對於同乙個快取,這個貌似不可能(不可能我這個do的失效時間短,那個do的失效時間長),除非你搞兩個快取;要麼,載入快取的時間點弄得分散一些,不要同時載入快取。
2. 不要讓所有相同的請求,都打在資料庫上。例如,有很多執行緒,都在請求某個相同的key,那麼,讓這些執行緒去搶著鎖住key。鎖住key的執行緒,去訪問資料庫,並更新快取。沒搶到的,則等著。這個方案的問題是,在沒有雪崩的時候,你也得去搶鎖,那效率就呵呵了。我感覺這個方案不怎麼現實。
快取雪崩和快取穿透
快取雪崩可能是因為資料未載入到快取中,或者快取同一時間大面積的失效,從而導致所有請求都去查資料庫,導致資料庫cpu和記憶體負載過高,甚至宕機。解決思路 1,採用加鎖計數,或者使用合理的佇列數量來避免快取失效時對資料庫造成太大的壓力。這種辦法雖然能緩解資料庫的壓力,但是同時又降低了系統的吞吐量。2,分...
快取穿透和快取雪崩
一 快取穿透 查詢乙個 必然不存在的資料,首先通過key到快取去查詢,不存在,然後到達db,這樣會對後台的db造成很大的壓力,這樣每次都會請求到達資料庫,這就是快取穿透 快取失效 如果快取集中在一段時間內失效,db的壓力凸顯 快取穿透避免的方法 1 最簡單粗暴的方法 如果在資料庫中查詢的也為null...
快取穿透和快取雪崩
也可以叫redis穿透和redis雪崩。redis是支援key value等多種資料結構的儲存系統,常用於快取。使用者進行查詢,發現redis記憶體資料庫中沒有資料,也就是快取沒有命中,就回去持久化層資料庫中進行查詢,發現也沒有,此次查詢失敗。當使用者過多,快取都沒有命中,每個都去持久化層資料庫中查...