緩存的設計方式

語言: CN / TW / HK

theme: scrolls-light

問題情況:

當有大量的請求到內部系統時,若每一個請求都需要我們操作數據庫,例如查詢操作,那麼對於那種數據基本不怎麼變動的數據來説,每一次都去數據庫裏面查詢,是很消耗我們的性能

尤其是對於在海量數據中進行數據操作的時候,如果都是從 DB 中進行加載,那這是在挑戰用户的耐性

簡單來看,例如我們要去小區裏面瞭解一個人在不在家,當沒有通訊工具的前提下,我們每一次都要經過小區們的保安,然後再到具體的單元樓,最終到了這家門口,最終才知道在不在家

如果我們換了一個比較優秀的保安,他知道當前小區裏面的特定的家裏面是否有人,那這個時候,如果我們直接去問小區保安,自然就無需跑冤枉路了,自然就提高了效率

此處簡單的就可以將優秀保安看做是一個緩存,我們每一次去訪問,就會先去訪問緩存 , 這樣就能極大的提高訪問效率和系統性能

可以看出,有一個優秀的保安相當重要

緩存的基本設計方式是什麼樣的

設計緩存自然也是為了解決系統是的低效問題,讓系統可以高性能,高併發

例如我們直接訪問單機的數據庫如mysql 也就是上千級別的 qps,如果是訪問 緩存的時候,就能達到上萬,上十幾萬,這差距不是一點半點,是一個質的飛越

緩存的設計實際上就是 DB 和 緩存操作順序以及誰來操作的事情,大體分為如下 4 種模式

  • Cache Aside
  • Read Through
  • Write Through
  • Write Behind Caching

上述四種模式, Cache Aside 用的方式是最常使用的,咱們後續細説

後續三種模式的含義是

Read Through

  • 是在查詢操作的時候更新緩存,若緩存失效了,則是由緩存服務器自己將數據加載到緩存中

Write Through

  • 是在更新數據庫的時候,如果命中了緩存,則先更新緩存,再由緩存服務器自己去更新數據,
  • 如果是沒有命中緩存,那麼就直接更新數據庫

Write Behind Caching 通過名字我們知道,是在寫到緩存操作之後才做些操作,實際上這種模式只更新緩存,不會更新數據庫,緩存服務器會以異步的方式將數據批量更新到數據庫中

很明顯,這種,模式速度自然會更快,可這種模式對於保證數據庫和緩存數據一致性問題,是個硬傷,且還會存在丟數據的情況,比如,咱們的緩存服務器掛掉了

Cache Aside 讀寫緩存模式是怎麼玩的

Cache Aside 讀寫模式緩存又是如何去處理的呢,一起來看看

Cache Aside 模式讀取數據的邏輯是這個樣子的:

讀取數據時

  • 先讀取緩存中的數據,如果緩存中有數據,則直接返回
  • 若緩存中沒有數據,則去讀數據庫中的數據,並將數據同步到緩存中

寫入數據時

  • 寫入數據庫,寫入成功時,將緩存的數據刪除掉

仔細的同學可能會思考並提出這樣的問題,如果我一個查詢操作,現在緩存中無數據,此時會去數據庫中查詢,在這個過程中,另外有一個寫入數據庫的操作,且操作完畢後,刪除了緩存,這個時候,第一個操作實際上從數據庫拿到的還是之前的老數據,並且會將數據放到緩存中,那麼此時的數據實際上是一個老數據,也可以理解是在髒數據

這個點其實我們就無需擔心了,大佬們已經論證過這種情況出現的概率極低

因為咱們的寫表操作是要鎖表的,且我們知道數據庫寫入操作比讀取操作要慢,也就是説,當同時有一個讀取和寫入 DB 的操作時,自然是寫入的操作是要後返回結果的,此處不要槓啥讀寫數據量不一致的情況,咱們做對比,自然是在同等條件下比較咯

從圖中我們知道,同等條件下,先進行查詢 DB 的操作,過程中,來了一個寫入 DB 操作,自然是 查詢操作先返回,寫入操作再返回結果

其實此處,有的做法是,寫入數據的時候,寫入成功,同時也會將數據同步到緩存中

那麼這種方式的引入,實際上從數據庫到緩存就有了 2 種情況了,一個是查詢操作,一個是寫入操作,那麼在實際操作中,我是可以加入分佈式鎖來進行處理,保證寫入數據庫的時候,同時也要寫入緩存,數據才可訪問,當然查詢 DB 操作也是一樣

緩存帶來了哪些問題?

那麼引入緩存除了可以帶來高性能,高併發,自然也是有會帶來一些問題的,例如:

  • 緩存擊穿
  • 緩存穿透
  • 緩存雪崩

如上 3 中情況,都是由於緩存這一層防線失守了,導致外部請求以各種各樣的形式,各種各樣的原因打到了數據庫上,導致出現的問題,詳細的 緩存擊穿,緩存穿透,緩存雪崩的出現情況,解決方式可以查看歷史文章 redis 緩存穿透,緩存擊穿,緩存雪崩

感謝閲讀,歡迎交流,點個贊,關注一波 再走吧

我正在參與掘金技術社區創作者簽約計劃招募活動,點擊鏈接報名投稿