iT邦幫忙

0

筆記:Redis 緩存穿透(Cache Penetration)

  • 分享至 

  • xImage
  •  

筆記:Redis 緩存穿透(Cache Penetration)

在系統架構的實作裡,常為了減輕後端資料庫的負擔,通常會在程式跟資料庫之間隔一層快取層(例:Redis or mongoDB...)。

常見的實作方式之一就是 request 打進來的時候,程式先查詢 Redis,若命中快取則直接回傳值;如果沒命中(Cache Miss),才進入資料庫進行查詢,然後將結果放一份在 Redis。

在一般「正常」情境下它可以運作良好,但在某些惡意攻擊下,可能就會被 client 端直接 DDos 到後端資料庫,也就是所謂的「緩存穿透」。


現象

所有 request 打進來請求的 key 在快取層裡面全部都找不到,都會繞過快取層直接打到後端的資料庫,造成資料庫的負擔變得很重。

這些所謂找不到的 key,有可能就是 client 端「刻意」的動作,故意打一些預期不會有的 key 值進來。


Solution

1.緩存空的結果物件

做法:當資料庫也查不到資料時,依然把 Key 存入 Redis,但值設定為一個空的結果物件,讓一樣的 key 在下一次的 request 進來時是打到快取層。
優點:實作簡單,能直接擋掉相同 Key 的重複攻擊。
缺點

  • 要多使用一些快取層的空間。尤其是當攻擊者產生的 Key 數量極大並且又是「不同」的時候,快取裡面就會有一堆垃圾了。
  • 因為這些空結果物件的 key 還是會設個 TTL,如果在短時間內設定了大量相同 TTL 的 key,它們的失效時間就會很接近,這就很有機會導致快取層的「雪崩」了。(傻瓜解法:TTL + random seconds 可緩解)

2.Key 值的合法性確認

做法:在還沒打到快取層之前,先用邏輯來判斷 key 值是不是合法的。例如:ID 必須是 between 0 and Max(ID) + 10000 之類的。讓一些一看就是不合法的 key 值,沒有機會往後流。
優點:可以在「更前面」的地方就將這種 dirty request 直接攔掉。
缺點

  • 要看 key 值適不適合。如果 key 不是有序的數字型別(或一些特定的適業邏輯),基本上就不一定適用。
  • 所謂的「合法」範圍可能需要不斷的更新,不然很容易濫殺無辜。

3.先將合法的 key 存一份快取

做法:在快取層之前再擋一層。將所有可能存在的合法 Key (不用內容)再存一份快取。 request 打進來時先用這份 key 清單來判斷是否合法。
優點:空間換時間,要多存一份快取。
缺點:又要再多維護一份快取清單。當資料有異動時,這份 key 清單快取都有更新到。

4. 在 client 端打進來的 API 入口增加 throttle 機制

實作:可以用 IP or token 之類的唯一身份識別,來卡同一個身份在某段時間區間內可以打進來的 request 量。

缺點:要再實作一套 throttle 機制來達到這個效果


個人經驗

如果這條路確定只會有內部系統的「網內互打」,那只要源頭使用的 client 端設計沒有問題,基本上上面的問題不太會遇到才對。

但如果是會「對外」,上面這些解法就可以考慮補一下,避免後端的資料庫被打爆。

但這些解法都沒有所謂的最佳解,也都只能依當時的情境跟實際「可行性」來決定要用哪個方法。

比如說時間不允許、影響的 scope 過大、影響的功能太敏感 or 主管不允許……等,各種不同的阻力。這時候就都是在做一個所謂的 trade-off 了。


參考資料

巨型服務架構:分布式/資料庫優化/記憶體快取設計/IO模型


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言