預防快取穿透方案設計

vipshop_fin_dev發表於2018-10-09

預防快取穿透方案設計

前言

最近公司有一些公司頻繁發生一些重大故障, 加上最近核心域凌晨比較多的一些快取超時(Caused by: net.spy.memcached.internal.CheckedOperationTimeoutException: Timed out waiting for operation),不得不讓人提高警惕。此時間段超時比較多,是因為該時間段是快取預熱高峰期。

排查有3種原因:

  1. memcached服務端支援的併發連線數已滿,spymemcache客戶端操作超時;
  2. memcache客戶端新增獲取資料時,主要spymemcache是基於nio非同步獲取的,所以當獲取資料時會把任務新增任務佇列等待執行,同時spymemcache也會做資料獲取的連結超時驗證,如果超過設定的超時時間(預設時間2500ms)就會報異常;
  3. 一次性get的key過多;

目前該超時在總請求數佔比相對很小,假如某個時間點,有大部分超時,將導致大部分快取穿透,對mysql資料庫造成巨大壓力。下面我們討論一些關於快取穿透的一些預防措施及相關設計。

快取穿透

我們先來了解一下快取穿透的定義

快取穿透是指使用者查詢資料,在資料庫沒有,自然在快取中也不會有。這樣就導致使用者查詢的時候,在快取中找不到,每次都要去資料庫再查詢一遍,然後返回空(相當於進行了兩次無用的查詢)。這樣請求就繞過快取直接查資料庫,這也是經常提的快取命中率問題.

業界有兩種常用預防快取穿透的方法

(1)採用布隆過濾器,將所有可能存在的資料雜湊到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力。
(2)如果一個查詢返回的資料為空(不管是資料不存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,這個根據業務上允許的時間為準。通過這個直接設定的預設值存放到快取,這樣第二次到快取中獲取就有值了,而不會繼續訪問資料庫,這種辦法最簡單粗暴!

針對第一種方法,大體的預防方案設計如下圖
在這裡插入圖片描述

在訪問所有資源(cache, storage)之前,將存在的key用布隆過濾器提前儲存起來,做第一層攔截,例如: 我們的價格服務有1億個sku, 我們可以對所有sku對應的key做一份布隆過濾器,這樣可以過濾掉一些不在本系統定價的一些請求,減少cache\storage資源的壓力。

虛擬碼如下

if (!bloomfilter.mightContain(key)) {
   return null;
}
String value = redis.get(key);
if (value == null) {
   return null;
} else {
   //這裡用mutex鎖實現單執行緒回源i)
   value = getFromCacheDb(key);
}
return value;

private String getFromCacheDb(String key) {
   String redis = null;
   String value = redis.get(key);
   if (value == null) {
      value = db.get(key);
      redis.set(key, value, expire_secs);
      redis.del(key_mutex);
   } else{
      sleep(50);
      getFromCacheDb(key);//重試
   }
   return value;
}

一、布隆過濾器是什麼?

  1. 又快又小的處理方法
  2. 布隆過濾器(Bloom Filter):是一種空間效率極高的概率型演算法和資料結構,用於判斷一個元素是否在集合中(類似Hashset)。
  3. 它的核心一個很長的二進位制向量和一系列hash函式
  4. 陣列長度以及hash函式的個數都是動態確定的
  5. Hash函式:SHA1,SHA256,MD5…
    在這裡插入圖片描述

二、優勢和劣勢

優勢

  1. 全量儲存但是不儲存元素本身,在某些對保密要求非常嚴格的場合有優勢;
  2. 空間高效率
  3. 插入/查詢時間都是常數O(k),遠遠超過一般的演算法

劣勢

  1. 存在誤算率(False Positive),隨著存入的元素數量增加,誤算率隨之增加;
  2. 一般情況下不能從布隆過濾器中刪除元素;
  3. 陣列長度以及hash函式個數確定過程複雜;

參考文章:
https://en.wikipedia.org/wiki/Bloom_filter
http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html
https://github.com/erikdubbelboer/Redis-Lua-scaling-bloom-filter
https://www.cnblogs.com/atomicbomb/p/8979582.html

相關文章