緩存常見問題
緩存更新方式
這是決定在使用緩存時就該考慮的問題。
緩存的數據在數據源發生變更時需要對緩存進行更新,數據源可能是 DB,也可能是遠程服務。更新的方式可以是主動更新。數據源是 DB 時,可以在更新完 DB 后就直接更新緩存。
當數據源不是 DB 而是其他遠程服務,可能無法及時主動感知數據變更,這種情況下一般會選擇對緩存數據設置失效期,也就是數據不一致的最大容忍時間。
這種場景下,可以選擇失效更新,key 不存在或失效時先請求數據源獲取最新數據,然后再次緩存,並更新失效期。
但這樣做有個問題,如果依賴的遠程服務在更新時出現異常,則會導致數據不可用。改進的辦法是異步更新,就是當失效時先不清除數據,繼續使用舊的數據,然后由異步線程去執行更新任務。這樣就避免了失效瞬間的空窗期。另外還有一種純異步更新方式,定時對數據進行分批更新。實際使用時可以根據業務場景選擇更新方式。
數據不一致
第二個問題是數據不一致的問題,可以說只要使用緩存,就要考慮如何面對這個問題。緩存不一致產生的原因一般是主動更新失敗,例如更新 DB 后,更新 Redis 因為網絡原因請求超時;或者是異步更新失敗導致。
解決的辦法是,如果服務對耗時不是特別敏感可以增加重試;如果服務對耗時敏感可以通過異步補償任務來處理失敗的更新,或者短期的數據不一致不會影響業務,那么只要下次更新時可以成功,能保證最終一致性就可以。
緩存穿透
緩存穿透。產生這個問題的原因可能是外部的惡意攻擊,例如,對用戶信息進行了緩存,但惡意攻擊者使用不存在的用戶id頻繁請求接口,導致查詢緩存不命中,然后穿透 DB 查詢依然不命中。這時會有大量請求穿透緩存訪問到 DB。
解決的辦法如下。
- 對不存在的用戶,在緩存中保存一個空對象進行標記,防止相同 ID 再次訪問 DB。不過有時這個方法並不能很好解決問題,可能導致緩存中存儲大量無用數據。
- 使用 BloomFilter 過濾器,BloomFilter 的特點是存在性檢測,如果 BloomFilter 中不存在,那么數據一定不存在;如果 BloomFilter 中存在,實際數據也有可能會不存在。非常適合解決這類的問題。
緩存擊穿
緩存擊穿,就是某個熱點數據失效時,大量針對這個數據的請求會穿透到數據源。
解決這個問題有如下辦法。
- 可以使用互斥鎖更新,保證同一個進程中針對同一個數據不會並發請求到 DB,減小 DB 壓力。
- 使用隨機退避方式,失效時隨機 sleep 一個很短的時間,再次查詢,如果失敗再執行更新。
- 針對多個熱點 key 同時失效的問題,可以在緩存時使用固定時間加上一個小的隨機數,避免大量熱點 key 同一時刻失效。
緩存雪崩
緩存雪崩,產生的原因是緩存掛掉,這時所有的請求都會穿透到 DB。
解決方法:
- 使用快速失敗的熔斷策略,減少 DB 瞬間壓力;
- 使用主從模式和集群模式來盡量保證緩存服務的高可用。