本篇文章轉載自:石衫的架構筆記 微信公衆號redis
當咱們系統中使用到了緩存時,無論是一級緩存仍是多級緩存,它的做用就是業務系統在請求數據時,若是緩存中有,就從緩存中獲取;若是緩存中沒有,再去訪問DB,從而減小DB的壓力,縮短獲取數據的時間。那麼在使用緩存的過程當中,會出現哪些常見問題?咱們又如何來解決?數據庫
正常狀況下,咱們去查詢數據都是存在的。緩存
那麼請求查詢一條壓根在數據庫中就不存在的數據,即在緩存和數據庫中都沒有這條數據,可是請求每次在緩存中找不到,就去訪問數據庫,比較佔用和消耗數據庫的資源,這種現象,就叫作緩存穿透。服務器
黑客攻擊系統,經過緩存穿透,使用一個不存在的id,大量訪問系統的接口,系統由於緩存中沒有數據,而去大量的訪問數據庫,致使數據庫資源被無效佔用,影響甚至拖垮正常業務。微信
咱們知道,緩存服務器(redis)通常是經過key value來存儲緩存數據的。當有請求過來,會經過key在緩存中查找數據。而發生緩存擊穿的問題,是由於緩存服務器中沒有存儲這些無效數據的key,從而致使無效請求訪問到數據庫。架構
那麼咱們能夠在這些請求訪問數據庫後,沒有獲得查詢結果時,在緩存服務器中存儲這個key,並設置它的值爲null,這樣當再次發生查詢這個key的請求時,緩存服務器就直接返回null,不去訪問數據庫了。併發
可是不要忘記,設置緩存失效時間,方便正確數據的錄入和更新。分佈式
BloomFilter相似於一個hbase set用來判斷某個元素(key)是否存在於某個集合中。高併發
這種方式在大數據應用場景比較多,好比Hbase(HBase是一個分佈式的、面向列的開源數據庫)中使用它去判斷數據是否在磁盤上。還有在爬蟲場景判斷url是否已經被爬取過。性能
這種方式能夠加在第一種方案中,在緩存以前再加一層BloomFilter去查詢key是否存在,若是不存在直接返回,存在再去查詢緩存或者DB。
針對大量惡意請求攻擊,若是key重複多,使用第一種方案是有效的。
若是惡意請求key不重複的多,則使用第二種方案最有效。
在日常高併發的系統中,大量的請求同事查詢到一個key時,此時這個key正好失效了,就會致使大量的請求都轉到數據庫上面了,這種現象咱們成爲緩存擊穿。
會形成數據庫訪問量忽然增大,壓力劇增。
上面的現象是多個線程同時去查詢數據庫的這條數據,那麼我能夠在第一個查詢數據的請求上使用一個 互斥鎖 來鎖住它。
其餘的線程走到這一步拿不到鎖就等待,等到第一個線程查詢到了數據,而後作緩存,後面的線程進來發現已經有緩存了,就能夠直接獲取緩存,而不訪問數據庫了。
在當某一時刻發生大規模的緩存失效的狀況,好比你的緩存服務宕機了,會有大量的請求直接請求數據庫上,結果數據庫就撐不住了,也跟着掛掉了。
使用集羣緩存,保證緩存服務器的高可用。
好比使用redis,就要考慮到 主 從 + 哨兵和 集羣 來避免redis的全面崩盤的狀況。
ehcahe本地緩存 + Hystrix限流&降級,避免DB被打死
使用ehcahe本地緩存的目的也是考慮在redis集羣徹底不可用的時候,ehcahe本地緩存還能支撐一陣。
使用Hystrix進行限流&降級,好比一秒來了5000個請求,咱們假設只能有一秒2000個請求經過這個組件,那麼其餘3000個請求就會走限流邏輯。
而後去調用咱們本身開發的降級組件,好比設置一些默認值之類的,以此來保護DB不會被大量請求搞的宕機。
開啓redis的持久化機制,恢復緩存服務器集羣
緩存服務器重啓,根據配置的持久化策略,會讀取日誌文件,恢復內存中的數據。
咱們在設置緩存的時候,通常也會設置緩存的失效時間,過了這個時間,緩存失效了。
若是這批數據,是請求量比較高的,那麼請求就會轉到數據庫去,數據庫壓力瞬間增大,性能降低甚至宕機。
就是緩存擊穿現象。
避免熱點數據集中失效,在設置緩存時間時,咱們讓它們的失效時間錯開。好比在一個基礎的時間上加上或減去一個範圍內的隨機值。
結合上面提到的請求,在高併發請求中,在第一個請求去查詢數據庫的時候對它加一個互斥鎖,其他的查詢請求都會被組塞住,直到鎖被釋放,從而保護數據庫。
可是也是由於它會阻塞其餘線程,系統的吞吐量也會降低,須要結合業務中的實際須要來考慮要不要這麼作。