1. 程式人生 > 其它 >力扣簡205 同構字串 半* ?

力扣簡205 同構字串 半* ?

如何應對快取穿透、快取擊穿、快取雪崩問題

1.Key的過期淘汰機制

Redis可以對儲存在Redis中的快取資料設定過期時間。
比如我們獲取的簡訊驗證碼一般十分鐘過期,我們這時候就需要在驗證碼存進Redis時新增一個key的過期時間
但是這裡有一個需要格外注意的問題就是:並非key過期時間到了就一定會被Redis給刪除。

1.1 定期刪除

Redis預設是每隔100ms就隨機抽取一些設定了過期時間的Key,檢查其是否過期,如果過期就刪除。
為什麼是隨機抽取而不是檢查所有key?因為你如果設定的key成千上萬,每100毫秒都將所有存在的key檢查一遍,會給CPU帶來比較大的壓力。

1.2 惰性刪除

定期刪除由於是隨機抽取可能會導致很多過期Key到了過期時間並沒有被刪除。
所以使用者在從快取獲取資料的時候,redis會檢查這個key是否過期了,如果過期就刪除這個key。這時候就會在查詢的時候將過期key從快取中清除。

1.3.記憶體淘汰機制

僅僅使用定期刪除+惰性刪除機制還是會留下一個嚴重的隱患:
如果定期刪除留下了很多已經過期的key,而且使用者長時間都沒有使用過這些過期key,導致過期key無法被惰性刪除,從而導致過期key一直堆積在記憶體裡
最終造成Redis記憶體塊被消耗殆盡。
那這個問題如何解決呢?這個時候Redis記憶體淘汰機制應運而生了。Redis記憶體淘汰機制提供了6種資料淘汰策略:
僅僅使用定期刪除+惰性刪除機制還是會留下一個嚴重的隱患:如果定期刪除留下了很多已經過期的key,而且
使用者長時間都沒有使用過這些過期key,導致過期key無法被惰性刪除,從而導致過期key- -直堆積在記憶體裡,最終
造成Redis記憶體塊被消耗殆盡。那這個問題如何解決呢?這個時候Redis記憶體淘汰機制應運而生了。Redis記憶體淘汰
機制提供了6種資料淘汰策略:

  • volatile-lru: 從已設定過期時間的資料集中挑選最近最少使用的資料淘汰。
  • volatile-tt1: 從已設定過期時間的資料集中挑選將要過期的資料淘汰。
  • volatile-random:從已設定過期時間的資料集中任意選擇資料淘汰。
  • allkeys-lru: 當記憶體不足以容納新寫入資料時移除最近最少使用的key。
  • allkeys-random:從資料集中任意選擇資料淘汰。
  • noenviction (預設) :當記憶體不足以容納新寫入資料時,新寫入操作會報錯

2.快取擊穿

首先我們來看下請求是如何取到資料的:
當接收到使用者請求,首先先嚐試從Redis快取中獲取到資料,如果快取中能取到資料則直接返回結果,
當快取中不存在資料時從DB獲取資料,如果資料庫成功取到資料,則更新Redis,然後返回資料

定義:高併發的情況下,某個熱門key突然過期,導致大量請求在Redis未找到快取資料

進而全部去訪問DB請求資料,引起DB壓力瞬間增大。

解決方案:快取擊穿的情況下一般不容易造成DB的宕機,只是會造成對DB的週期性壓力。對快取擊穿的解決方案一般可以這樣:

  • Redis中的資料不設定過期時間,然後在快取的物件上新增一個屬性標識過期時間,每次獲取到資料時,
    校驗物件中的過期時間屬性,如果資料即將過期,則非同步發起一個執行緒 主動更新快取中的資料。
    但是這種方案可能會導致有些請求會拿到過期的值,就得看業務能否可以接受,
  • 如果要求資料必須是新資料,則最好的方案則為熱點資料設定為永不過期,然後加一個互斥鎖保證快取的單執行緒寫。

3.快取穿透

定義:快取穿透是指查詢快取和DB中都不存在的資料。

比如通過id查詢商品資訊,id一般大於0,攻擊者會故意傳id為-1去查詢,由於快取是不命中
則從DB中獲取資料,這將會導致每次快取都不命中資料導致每個請求都訪問DB,造成快取穿透。

解決方案:

  • 利用互斥鎖,快取失效的時候,先去獲得鎖,得到鎖了,再去請求資料庫。沒得到鎖,則休眠一段時間重試
  • 採用非同步更新策略,無論key是否取到值,都直接返回。value值中維護一個快取失效時間,快取如果過期,異
    步起一個執行緒去讀資料庫,更新快取。需要做快取預熱(專案啟動前,先載入快取)操作。
  • 提供一個能迅速判斷請求是否有效的攔截機制,比如,利用布隆過濾器,內部維護一系列合法有效的key。
    迅速判斷出,請求所攜帶的Key是否合法有效。如果不合法,則直接返回。
  • 如果從資料庫查詢的物件為空,也放入快取,只是設定的快取過期時間較短,比如設定為60秒。

4.快取雪崩

定義:快取中如果大量快取在一段時間內集中過期了,這時候會發生大量的快取擊穿現象,所有的請求都落在了

DB上,由於查詢資料量巨大,引起DB壓力過大甚至導致DB宕機。

解決方案:

  • 給快取的失效時間,加上一個隨機值,避免集體失效。如果Redis是叢集部署,將熱點資料均勻分佈在不同的
    Redis庫中也能避免全部失效的問題
  • 使用互斥鎖,但是該方案吞吐量明顯下降了。
  • 設定熱點資料永遠不過期。
  • 雙快取。我們有兩個快取,快取A和快取B。快取A的失效時間為20分鐘,快取B不設失效時間。
    自己做快取預熱操作。然後細分以下幾個小點
    1.從快取A讀資料庫,有則直接返回
    2.A沒有資料,直接從B讀資料,直接返回,並且非同步啟動一個更新執行緒。
    3.更新執行緒同時更新快取A和快取B。