高可用服務設計之如何應對緩存穿透

背景

用戶中心是受權邏輯與用戶信息相關邏輯構建的應用。分佈式系統中,大多數業務都須要和用戶中心打交道,爲了保證用戶中心服務的高可用,避免不了作緩存、導入搜索引擎從而下降數據庫的壓力。然而有些不通過用戶中心受權的業務場景查詢用戶中心的數據,可能引起大量無效的查詢,發生緩存穿透,直接對搜索引擎和數據庫形成壓力。如何解決用戶中心緩存穿透的問題呢?接下來就着重說一下布隆過濾器是怎麼「隔檔」這些無效查詢的java

緩存穿透

緩存穿透是指用戶查詢數據,在數據庫沒有,天然在緩存中也不會有。這樣就致使用戶查詢的時候,在緩存中找不到對應keyvalue,每次都要去數據庫再查詢一遍,而後返回空(至關於進行了兩次無用的查詢)。這樣請求就繞過緩存直接查數據庫redis

布隆過濾器

基本概念

  • 布隆過濾器(Bloom Filter)是1970年由布隆提出的。它其實是一個很長的二進制向量(位圖)和一系列隨機映射函數(哈希函數)。
  • 布隆過濾器能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除困難。

特色

  • 空間效率高和查詢效率高的機率型數據結構。
  • 對於一個元素檢測是否存在的調用,BloomFilter會告訴調用者兩個結果之一:可能存在或者必定不存在。
  • 一個很長的二進制向量 (位數組)
  • 一系列隨機函數哈希)。
  • 有必定的誤判率(哈希表是精確匹配)

原理

布隆過濾器(Bloom Filter)的核心實現是一個超大的位數組和幾個哈希函數。假設位數組的長度爲m,哈希函數的個數爲k算法

(1) 添加元素過程
數據庫

  • 將要添加的元素給k個哈希函數。
  • 獲得對應於位數組上的k個位置。
  • 將這k個位置設爲1。

(2) 查詢元素過程
網頁爬蟲

  • 將要查詢的元素給k個哈希函數。
  • 獲得對應於位數組上的k個位置。
  • 若是k個位置有一個爲0,則確定不在集合中。
  • 若是k個位置所有爲1,則可能在集合中。

相關公式

很顯然,根據布隆過濾器的原理和特性,bit數組大小和哈希函數的個數都會影響誤判率。那麼布隆過濾器是如何權衡bit數組大小和哈希函數個數的呢?小程序

布隆過濾器bit數組大小爲m,樣本數量爲n,失誤率爲p微信小程序

假設樣本容量n=5000W,誤判率是0.03,那麼所須要的內存空間大小是m = -5000W * -3.057 / (0.7)^2 318,437,500 39.8MB數組

演示

(1)參考地址緩存

https://www.jasondavies.com/bloomfilter微信

(2)可能存在

 

 

 

 (3)必定不存在

Guava Bloom Filter

Guava中,布隆過濾器的實現主要涉及到2個類, BloomFilterBloomFilterStrategies首先來看一下 BloomFilter的成員變量。須要注意的是不一樣Guava版本的 BloomFilter實現不一樣。

  • BitArrays 是定義在BloomFilterStrategies中的內部類,封裝了布隆過濾器底層bit數組的操做。
  • numHashFunctions表示哈希函數的個數,即上文公式提到的k
  • Funnel主要是把任意類型的數據轉化成Java基本數據類型(primitive value,如charbyteint……),默認用java.nio.ByteBuffer實現,最終均轉化爲byte數組。
  • Strategy是定義在BloomFilter類內部的接口,代碼以下,有3個方法,put(插入元素),mightContain(斷定元素是否存在)和ordinal方法(能夠理解爲枚舉類中那個默認方法)。

 

 

BloomFilterStrategies類,首先它是實現了BloomFilter.Strategy 接口的一個枚舉類,其次它有兩個2枚舉值,MURMUR128_MITZ_32MURMUR128_MITZ_64,分別對應了32位哈希映射函數和64位哈希映射函數,後者使用了murmur3 hash生成128哈希值,具備更大的空間,不過原理是相通的

MURMUR128_MITZ_64實現原理能夠參考(http://rrd.me/gDkD5)。

 

BitArrayguava bloom filter底層bit數組的一個實現類。Guava使用的是一個long型數組實現了相似BitSet的數據結構。第一個構造函數傳入了一個bit位的位數bits,而後bits除以64並向上取整獲得long型數組的大小。getset操做根據bit位的索引index,找到對應的操做對象data[index >>> 6](等價於data[index / 64]),分別跟(1L << index)與操做和或操做相應的結果。

Redis Bloom Filter

分佈式系統直接使用guava bloom filter在某些業務場景下不是很方便,既然是分佈式環境,最好仍是經過分佈式緩存封裝一版布隆過濾器。

經過對guava bloom filter的分析,由單機版改形成分佈式版,只須要從新實現三個guava bloom filter的三個類(BloomFilterBloomFilterStrategiesBitArray)。

RedisBitArray改造不是很麻煩,只須要引入操做分佈式緩存的JedisCluster對象就行了。getset操做對應JedisCluster對象的getbitsetbit操做(針對String類型的值,Redis經過 位操做 實現了BitMap數據結構)。

BloomFilterBloomFilterStrategies的改造相對比較簡單,這裏就不詳細說明了。

Routing Bloom Filter

爲何要有路由布隆過濾器?經過上面的公式能夠知道,當要插入的樣本數量n越大,那麼須要分配的內存容量m也會越大。也就是布隆過濾器的不當使用極易產生大 Value,增長 內存溢出或者阻塞風險,所以生成環境中建議對體積龐大的布隆過濾器進行拆分,拆分的規則咱們定義爲按照必定的路由規則對應到不一樣的布隆過濾器。

(1) 設計方案

 

(2) 路由策略

  • routing方法根據樣本計算出路由key值。
  • exceptedInsertions方法根據樣本獲取到路由key值,而後計算指望插入的樣本數量。

 

(3) 成員變量

  • ROUTE_MAP是本地緩存,存儲RoutingStrategy對象routing方法計算出的路由key值以及對應的RedisBloomFilter實例。
  • routingStrategy是路由策略RoutingStrategy實例。
  • bfRedisKeyPrefixRedis布隆過濾器bit數組在redis中對應的key值前綴。
  • bfKeysMappingRedisKey存儲了全部Redis布隆過濾器bit數組在redis中對應的key(即bfRedisKeyPrefix + 路由key)的集合。

 

(4) put操做

獲取當前樣本對象的routeKeyROUTE_MAPcomputeIfAbsent方法根據routeKey獲取對應的Redis Bloom Filter,若是不存在則建立一個新的Redis Bloom Filter對象實例並保存到ROUTE_MAP中。變量bloomFilterRedisKey = bfRedisKeyPrefix + routeKey,也就是Redis Bloom Filter bit數組在redis中存儲的key值,最後保存在分佈式緩存的集合中(即bfKeysMappingRedisKey對應的集合)。

 

(5) mightContain操做

put操做的流程基本一致,在獲取routeKey對應的Redis Bloom Filter實例的時候,若是不存在須要判斷分佈式緩存bfKeysMappingRedisKey對應的集合中是否存在bloomFilterRedisKey,若是不存在說明put操做沒有建立對應的Redis Bloom Filter實例,直接返回null

 

(6) 監控信息

  • approximateElementCount,布隆過濾器中可能存在的元素個數。
  • bitSize,布隆過濾器bit數組大小。
  • bitCount,布隆過濾器bit數組中bit位是1的數量。
  • keyLength,布隆過濾器bit數組經過strlen統計的長度。

注:布隆過濾器的bit數組在redis中對應的數據類型是String哦!

應用場景

  • 網頁爬蟲對URL的去重。
  • 黑名單,垃圾郵件過濾。
  • 解決數據庫緩存擊穿。

實際應用

消息中心給用戶推送消息的時候,是按照先微信小程序用戶,不然公衆號用戶串行邏輯來執行的(大多數消息都是按照用戶手機號推送的)。小程序的用戶體系相對公衆號的用戶體系是較少的,並且小程序用戶訂閱消息的量級增加的緩慢。這就出現了不少不是小程序用戶的查詢請求,也就是出現了上面提到 緩存穿透 現象,無形之中會增長搜索引擎和數據庫壓力。

小程序用戶查詢服務集成了布隆過濾器,很優雅的解決了緩存穿透的問題。業務上線初期,天天大約有200W300W的請求,能夠過濾掉90%以上的無效用戶查詢請求。看着這鮮明的效果,欣喜若狂,心想着這方案集成的太完美了,真香!

源碼參考

請關注微信訂閱號(算法和技術SHARING),回覆:bloomfilter, 即可查看。

參考資料

https://www.jianshu.com/p/2104d11ee0a2

https://zhuanlan.zhihu.com/p/43263751

http://www.javashuo.com/article/p-coqwgbmg-kv.html

https://www.jianshu.com/p/44b4b42931d4

相關文章
相關標籤/搜索