作者 | 艾小仙 責編 | 張文
頭圖 | CSDN 下載自視覺中國
你應該從網上看過太多的文章說緩存穿透怎麼解決?無非就是布隆過濾器,緩存空值什麼的。
但是,更深入的一個問題,緩存空值有沒有問題?如果緩存的空值太多怎麼辦?
如果用的 redis,那麼太多的空值會不會打爆你的 redis?如果用的本地緩存,會不會打爆你的內存?繼而引發的問題就是還是會打爆你的數據庫。
從線上問題說起
前不久,我們線上環境壓測,在 QPS 壓倒 2W 之後 RT 達到了幾十秒,排查後發現是 redis 的連接數不夠導致大量的連接超時。
經過考慮之後,我們最終決定棄用 redis 緩存的方案,改為本地緩存。
因為我們緩存的都是一些配置信息,實際上幾個月都不太可能修改,而 redis 配置的連接數是 200,5 分鐘超時,數據量實際上也就只有幾千條而已,實際上來說並沒有很大的必要,本地緩存完全就可以解決問題了。
本地緩存使用 Guava 的 LoadingCache 實現。
但是修改完之後,壓測之後還是發現有接口全部走到數據庫查詢。
先排查代碼,是否是代碼的 BUG 導致實際沒有生效,後來發現實際上發生了緩存穿透,壓測使用了一些數據庫中不存在的記錄,導致了穿透的問題,實際上這個問題在使用 redis 的時候也一直存在,只是由於連接數的問題一直沒有發現而已。
接下來就是考慮怎麼解決的問題?
由於我們都是緩存的一些配置信息,幾千條數據而已,最終考慮簡單解決的辦法。直接把所有的 key 全部從數據庫查出來緩存下來,查數據庫之前直接根據 key 過濾一把,如果不存在就直接返回,不要走數據庫查詢了。
當然,這是由於我們的場景比較簡單,這樣直接處理就行了,那麼,如果再複雜一點,比如上億的緩存數據呢?
解決方案
2.1 前置過濾
如果說類似我這種比較簡單的一些緩存,使用我上面說的解決方案也可以,還有一些緩存的 key 是比如 ID 之類,也可以根據一定的範圍規則去提前過濾,比如緩存的 key 明確知道在 1-10 萬的範圍之後,那麼過濾掉在這個範圍之外的請求直接返回就可以了。
當然,很明顯這種簡單的規則過濾適用於數據量不是很大,並且數據不會頻繁發生改變的情況。
2.2 布隆過濾器
對於上述場景,因為數據量很小,簡單的代碼實現緩存即可,如果說數據量很大的話,比如有一億個 key,使用布隆過濾器就是個更優解。
我們可以每天定時把所有的配置信息從數據庫中查詢出來構建成 bitmap。
關於布隆過濾器我前面的文章也有寫過,貼上之前的圖一張,如果查詢的位置都是 1 的話說明 key 存在,反之只要有一個 0 則說明肯定不存在。
使用布隆過濾器的缺點也很明顯,存在一定概率的誤判。當然,既然用了,對於誤判比例、內存佔用等等問題應該事先評估好。
2.3 緩存空值
這個是網上說爛的問題,但是緩存空值的空值太多明顯也是有問題的,再進一步解決方案就是快速過期。
一般來說,普通的緩存的寫法如下,先查緩存,如果緩存存在則直接返回,如果緩存沒有則去數據庫查詢,結果不是空就保存到緩存中。
改進版的寫法就是緩存空對象,針對空的數據,設置過期時間,比如 10 分鐘,快速過期,防止太多的空值問題。
但是這個解決方案仍然有點小問題,就是短暫的數據不一致的問題。
想象一下如果緩存的空值這時候實際上已經有值了,那麼在過期時間的這段時間內就可能存在短暫的數據不一致。
總結
緩存穿透的問題總結下來就是三點,這三個方式不是說是隔離的解決方案,他們可以結合在一起使用。
首先看數據量,如果數據量很小並且沒有頻繁變更的話,選擇前置過濾的方式,根據具體的業務規則來處理就可以。
如果數據量大的話,可以選擇使用布隆過濾器,但是存在一定概率的誤判。
通過前置的攔截,應該攔截住大部分的流量,避免直接打爆數據庫。
最後,可以使用緩存空值並且設置快速過期的方式來作為一個兜底的方案。
如果還有問題,那麼就是限流、降級了。
程序員如何避免陷入“內卷”、選擇什麼技術最有前景,中國開發者現狀與技術趨勢究竟是什麼樣?快來參與「2020 中國開發者大調查」,更有豐富獎品送不停!
轉載請超鏈接註明:頭條資訊 » 更復雜的緩存穿透怎麼解決
免責聲明 :非本網註明原創的信息,皆為程序自動獲取互聯網,目的在於傳遞更多信息,並不代表本網贊同其觀點和對其真實性負責;如此頁面有侵犯到您的權益,請給站長發送郵件,並提供相關證明(版權證明、身份證正反面、侵權鏈接),站長將在收到郵件24小時內刪除。