後端工程師面試考什麼 — 快取模式篇(cache patterns)

覺得我們的內容實用嗎?MyApollo 電子報讀者募集中!歡迎訂閱電子報!

實務上快取有多種模式(patterns),每 1 種都有適合的應用場景,大多數後端工程師應該只對 Cache Aside 快取模式相對熟悉,較少接觸其他種模式,不過實際面試時也可能被詢問到 Cache Aside 以外的模式,原因在於 Cache Aside 模式沒辦法在所有的應用場景都完美運作,因此需要對常見的 Cache 模式有基本認識,才有辦法提出合理的解答。

本文將介紹 6 種快取模式。

Cache Aside 模式#

Cache Aside 是後端工程師最常用使用的快取方式,它的運作如下圖所示:

cache-aside.png

應用(application)會先查詢 cache server 有沒有相關的快取,如果有的話,就拿出來直接回應給使用者;如果沒有快取,就對資料庫進行查詢,再將查詢結果放到 cache server, 如此一來,下次再有相同的 request 的話,就能夠使用快取。

如果你在程式碼中看到類似以下的模式,那就是使用 Cache Aside 模式:

def api(...):
    ...
    # step 1
    cache = get_cache(key)
    if cache:
       # step 2
       return response.ok(cache)
    # step 3, 4
    data = query_database()
    # step 5
    set_cache(key, data, ttl)
    return response.ok(data)

p.s. 上述程式碼的註解數字對應的是 Cache Aside 圖上的數字

Cache Aside 適合用在符合以下 2 種情況的資料:

  1. 快取不常變動
  2. 快取失效也不會造成資料庫負擔驟增
  3. 讀取遠多於寫入的情況

如果你的資料不符合上述條件,最好別使用 Cache Aside, 詳見後端工程師經驗談 — 有時候用 Cache Aside 是個壞主意 。

使用 Cache Aside 的缺點是初次建立快取或快取失效時會有較長的 latency, 因為它需要花費 1 次詢問 cache server 的 latency, 確認沒有 cache 存在才會向資料庫送出查詢,最後再將資料快取起來。

另外, Cache Aside 也會有資料庫內的資料不一致的情況,因為應用端只有在快取失效或不存在時,才會對資料庫進行查詢並建立快取,所以資料庫內的資料已經更新時, cache server 內的資料就可能還是舊的資料,需要等到快取失效或者主動更新快取才能保持資料一致。

Refresh-Ahead 模式#

如果資料快取失效會造成資料庫負擔驟增的情況,或者每次產生快取會有較高的 latency 產生時,可以改採用 Refresh-Ahead 模式。

refresh-ahead.png

Refresh-Ahead 模式會在快取失效前,主動更新快取,所以應用端不需要判斷是否有快取資料存在,也不需要由應用端更新快取。

使用 Refresh-Ahead 模式的好處在於:

  1. 減少應用端的 latency
  2. 應用端的實作邏輯也相對單純,只要直接讀取快取即可

Refresh-Ahead 不適合用在快取大量異質資料的情況,例如使用 Refresh-Ahead 快取全站使用者資料就相當不適合,因為不是所有使用者都會上線,因此不僅浪費快取伺服器的資源,也容易拉長快取更新時間,甚至更新快取時對資料庫造成負擔。

Read Through 模式#

Read Through 模式與 Cache Aside 有點類似,不過差別在於應用端(application)只需要跟 cache server 溝通,如果 cache server 中有快取存在,即可直接回覆應用端;如果 cache server 中沒有快取存在,則 cache server 會代為向資料庫查詢,並進行快取。

read-through.png

使用 Read Through 模式與 Cache Aside 模式一樣會有初次建立快取、快取失效時的 latency 問題。

同樣地, Read Through 模式也會 Cache Aside 模式一樣有與資料庫資料不一致的問題。

使用 Read Through 模式的優點在於應用端程式邏輯只需與 cache server 互動即可,不像 Cache Aside 還需要實作建立快取的邏輯。

Write Through 模式#

Write Through 與 Read Through 模式行為相反,它是寫入資料時,直接寫入 cache server, 由 cache server 負責與資料庫更新,是一環扣一環的模式。

write-through.png

p.s. 你也可以想像 cache server 之後就是 1 個整體,我們只跟它互動,剩下的工作它會自己處理

RedisGears/rgsync 屬於這種,資料寫入 Redis 之後,它會再幫忙寫入資料庫。

使用 Write Through 模式的好處在於快取資料與資料庫之間有更高的一致性,只要資料庫有更新, cache server 內的資料必然會跟著更新,適合寫入之後立馬要頻繁進行讀取的情況。

缺點則是資料被不分青紅皂白一律快取起來,會造成 cache server 資源的浪費。

通常 Write Through 模式會與 Read Through 模式搭配,統一由 cache server 負責資料的讀取與寫入,應用端的實作邏輯也是相對單純,只要直接讀取快取即可。

Write Behind / Write Back 模式 / 非同步快取寫入模式#

由於 Write Through 模式同時需要寫入 cache server 與資料庫,所以勢必會增加一些 latency, 因此 Write Behind / Write Back 模式進一步把寫入資料庫的部分改為非同步方式,藉此降低 latency 。

write-behind.png

Write Behind / Write Back 模式相當適合寫入操作很頻繁的情況,非同步的寫入資料庫方式也可以藉由控制寫入速度等手段,降低資料庫負擔。

不過 Write Behind 的缺點在於有資料遺失(data loss)的可能性,例如 cache server 更新資料庫之前故障,就有可能造成最新的資料遺失。

Write Around 模式#

Write Around 模式是單指資料只寫入資料庫,不寫入 cache server 的情況,例如下列程式碼:

def write_around(db, data):
    db.write(data)

至於讀取資料之後要怎麼快取,就依照需求搭配,例如 Write Around 搭配 Read Throught:

write-around-read-through.png

Write Around 模式的優勢在於可以避免像 Write Through 那般不分青紅皂白都快取的情況。

總結#

每 1 種快取模式都有各自的優缺點,沒有絕對完美的模式,只有適合的應用情境,因此理解各種模式與其適合的應用情境相當重要,如此才能套用合適的模式解決問題。

以上!

Enjoy!

References#

RedisGears/rgsync: A Write-Behind and Write-Through recipe for RedisGears

Caching patterns - Database Caching Strategies Using Redis

Write-behind, write-through, and read-through caching | Redis Documentation Center

Overview of Caching, Distributed Cache, Caching Patterns & Techniques

覺得內容實用嗎?

MyApollo 電子報讀者募集中!新文章與技術新知會直接寄到你的信箱。

訂閱電子報

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧!PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

馬上試用 PomodoRoll

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!

Buy me a coffee

輸入關鍵字開始搜尋