跳轉到

Cache

緩存讀寫機制

Read-Aside

img

應用直接去緩存中找數據,命中緩存則直接返回,如果未命中緩存,則需要先去資料庫中查詢數據,並將查詢到的數據儲存到緩存中

Read-Through

img

Read-Through 的方式與 Read-Aside 的方式很接近,區別在於,Read-Aside 是通過應用程式來更新緩存中的數據,而 Read-Through 則是通過緩存自身來更新數據,也就是說應用和資料庫之間不直接進行連接

Write-Around

img

並不會用到緩存,而是會直接寫入到資料庫中

Write-Through

img

先將數據寫入到緩存中,然後由緩存將數據存入到資料庫中

Write-Through 與 Read-Through 相結合可以很好的解決緩存和資料庫中數據不一致的問題,Write-Through 每次都會先更新緩存中的數據,所以每次讀到的數據也是最新的

Write-Behind

img

寫了多次緩存後才會寫一次資料庫,可以大大減輕伺服器的壓力,但如果緩存出現了問題,那麼緩存中這部分沒有持久化的數據就會丟失

Question

Cache-Aside 為什麼要配合 Write-Around 而不能和 Write-Through 一起使用

因為 Write-Through 會先更新緩存,而如果這時剛好有另外一個線程將資料庫中舊的數據讀取出來將緩存中新的數據覆蓋,就會造成數據錯誤,而使用 Write-Around 就不會出現這個問題

缓存淘汰策略

  1. LRU (Least Recently Used)

  2. LFU (Least Frequently Used)

  3. MRU (Most Recently Used)

使用快取所需要留意的事情

緩存雪崩(Cache Avalanche)

緩存在同一時刻全部失效,造成瞬時 DB 請求量大、壓力驟增,引起雪崩。緩存雪崩通常因為緩存服務器宕機、緩存的 key 設置了相同的過期時間等引起。

常見處理方式

  • 提高緩存可用性
1. 集群部署

通過集群來提升緩存的可用性,可以利用 Redis 本身的 Redis Cluster 或者第三方集群方案如 Codis 等。

2. 多級緩存

設置多級緩存,第一級緩存失效的基礎上,訪問二級緩存,每一級緩存的失效時間都不同。

  • 設置過期時間
3. 均勻過期

為了避免大量的緩存在同一時間過期,可以把不同的 key 過期時間隨機生成,避免過期時間太過集中。

4. 熱點數據永不過期
  • 熔斷降級
5. 服務熔斷

當緩存服務器宕機或超時響應時,為了防止整個系統出現雪崩,暫時停止業務服務訪問緩存系統。

6. 服務降級

當出現大量緩存失效,而且處在高並發高負荷的情況下,在業務系統內部暫時捨棄對一些非核心的接口和數據的請求,而直接返回一個提前準備好的 fallback 錯誤處理信息。

緩存擊穿(Cache Breakdown)

一個 key 在緩存過期的那一刻,同時有大量的請求進來,這些請求都會流向 DB,造成 DB 壓力驟增。

常見處理方式

1. 加鎖更新(高流量下)
  1. 從 cache 拿資料X,如有則直接回傳

  2. 拿到資料X的鎖

  3. 再次從 cache 拿資料X,如有則釋放鎖並直接回傳

  4. 從 DB 拿資料X

  5. 把資料X放回 cache

  6. 釋放鎖並回傳資料X

2. 將過期時間組合寫在 value 中

通過異步的⽅式不斷的刷新過期時間,防⽌此類現象。

緩存穿透(Cache Penetration)

查詢不存在的數據的大量請求流向 DB,造成 DB 壓力驟增。主要有兩種原因造成緩存穿透:

  • 自身業務代碼問題

  • 惡意攻擊、爬蟲造成空命中

常見處理方式

1. 緩存空值/默認值

在數據庫不命中之後,把一個空對像或者默認值保存到緩存,之後再訪問這個數據,就會從緩存中獲取,這樣就保護了數據庫。但此做法有兩大問題

  • 空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內存空間(如果是攻擊,問題更嚴重),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。
  • 緩存層和儲存層的數據會有一段時間窗口不一致,可能會對業務有一定影響。例如過期時間設置為 5 分鐘,如果此時儲存層添加了這個數據,那此段時間就會出現緩存層和儲存層數據的不一致。這時候可以利用消息隊列或者其它異步方式清理緩存中的空對象。
2. Bloom Filter

在緩存之前先做一層過濾

Reference