整理了一些緩存相關的問題
術語
- 命中
根據百度百科的解釋,終端用戶訪問加速節點時,如果該節點有緩存住了要被訪問的數據時就叫做命中,如果沒有的話需要回原服務器取,就是沒有命中。取數據的過程與用戶訪問是同步進行的,所以即使是重新取的新數據,用戶也不會感覺到有延時。 命中率=命中數/(命中數+沒有命中數), 緩存命中率是判斷加速效果好壞的重要因素之一。簡單來說,就是先去讀取緩存,讀取到了就是命中了。
- 過期
過期有兩種,一種是時間過期,一種是淘汰過期。
時間過期好說,就是時間到了,被消除了。
所謂淘汰過期,只是在某些緩存服務器中對緩存內容大小進行了限制,當緩存接近於設置大笑時,會根據緩存服務的緩存策略進行操作,例如redis含有的一些淘汰策略:
- voltile-lru:從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰
- volatile-ttl:從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰
- volatile-random:從已設置過期時間的數據集(server.db[i].expires)中任意選擇數據淘汰
- allkeys-lru:從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
- allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰
- no-enviction(驅逐):禁止驅逐數據
緩存失效
也叫做緩存雪崩。某些項目可能會在配置項中寫一個固定的緩存過期時間,在併發比較高的情況下,可能會同時產生一些一些緩存鍵,這些緩存的的過期時候由於都是直接調取配置項,所以過期時間基本一致,這樣就會導致在某一時間這些緩存同時失效,請求全部到DB,DB可能會壓力過重。
解決方法:在緩存的時候給過期時間加上一個隨機值,這樣就會大幅度的減少緩存在同一時間過期。
緩存一致性
當數據時效性要求很高時,並且存在讀與寫場景都存在的併發場景,如何保證緩存的正確性?在寫操作中,是先刪除緩存還是操作完再重新覆蓋緩存?如果在集羣中,又如何保證副本緩存的同步?關於緩存一致性,內容稍微有點多,將在後期中詳細敘述。
緩存併發
當某一個緩存失效,通常的操作順序都是先查找數據,再進行操作,然後更新DB與cache。但在高併發的情況下,一個緩存過期會導致多個進程同時查找DB,如果緩存更新,對某個key有大量的併發請求,此時請求獲得的結果可能是更新之前也可能是更新之後,從而會導致“一致性”的問題。
解決辦法:由於緩存併發問題一般發生在查詢期間,所以當緩存失效的時候,對key加鎖。其他請求判斷到請求鎖存在,就等待,直到重新緩存完畢並解鎖。
緩存擊穿
指查詢一個一定不存在的數據,由於緩存是不命中時被動寫的,並且出於容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。當在流量較大時,出現這樣的情況,一直請求DB,很容易導致服務掛掉。
解決辦法:無論是否查詢到結果,都寫入緩存,但是查詢結果不存在的時候,設置過期時間稍短一點。
筆者認爲,在緩存失效前重新寫入緩存能有效的避免一些緩存問題,具體怎麼在緩存失效前重新寫入,方法有很多種,這裏就不討論了。
以上內容同步更新到了訂閱號【後端與web安全】(backend_websecurity)中,歡迎關注,不定期更新內容。轉載請以鏈接方式註明出處。