其實在之前的文章【快取,確實很香,卻也很受傷!】中,對快取穿透的引發緣由及應對策略做過簡要的描述。這篇文章將對這個問題再做下額外的擴充套件。
一、關於布隆過濾器
布隆過濾器支援兩種操作:1、新增元素;2、判斷元素是否存在。
布隆過濾器的特性:佔用少量記憶體過濾海量資料
判斷元素存在會返回兩種結果:
1、不存在:表示元素肯定不存在於布隆過濾器中。
2、存在:表示元素可能存在於布隆過濾器中。說可能,是因為這裡有個誤差,和布隆過濾器的容量及應用的演算法有關。
所以是要判斷存在還是判斷不存在,要結合實際應用中,能否接受誤差,及能夠接受多大的誤差來決定。
二、新聞過濾
你有一個業務爬蟲,不斷的爬取其它新聞網站的文章,然後釋出到自己的網站,控制重複文章的釋出就變得必不可少,海量的新聞資訊,怎麼過濾?
基於庫?庫負擔大,效率太低;
基於快取,儲存已釋出的所有新聞到快取?成本太高,得不償失。
新聞有時效價值,歷史新聞價值極低,顯然基於布隆過濾器的過濾是一個很好的選擇。
三、關係過濾
假如我們有一種業務場景,查詢兩者之間的某種關係資料,比如,a是b是什麼關係。
同學?老鄉?生意夥伴?
可能只是同學,或者同學+老鄉,或者同學+老鄉+生意夥伴,又或者什麼關係都沒有。
我們有資料庫儲存,也構建了相應的快取資料來加速響應。
但是當關係資料很少,基數很大時,已有的關係數據相對於所有的窮舉關係就變得很渺小,
這時候,快取所起到的所用就會變得很小,大部分時間不可避免的會產生穿透,進而進行入庫查詢,那這種問題怎麼解決呢?
1、控制查庫的時機
a)快取狀態:
基於開篇提到的我們之前文章,我們知道,可以把每次快取穿透入庫查詢不存在的資料儲存一份狀態到快取,這樣下次在查詢的時候就可以直接在快取層面攔截。
這是一個正常的解決方法,對於很多應用場景都很適用。但是對於我們上述列舉的情景,這種方法會帶來一些不可避免的隱患。
關係是一種兩者之間的關聯,上面我們講到過,窮舉的關係資料相對於基數物件是一種量級的膨脹,所以以快取不存在狀態來避免穿透會是一種成本無法控制的方式。
改進:雖然這種方式面對的是一個不確定量的快取需求,但是我們可以通過對狀態設定較短的過期時間來一定程度的進行規避。
b)基於過濾器:
開篇介紹過布隆過濾器的特性,我們可以將每次查詢不存在的查詢放入過濾器,對於過濾器過濾判定結果,不存在的則進一步直接入庫查詢,判定存在的,則走常規的邏輯,查快取,查庫。
說到這裡,可能有人會有疑問,對於已判定不存在關係放入過濾器的,之後業務中產生了關係的,難道還要每次都直接入庫查詢嗎?
這,確實是這種方式埋下的一個問題,但是相對於未應用過濾器之前,這種對於業務結果的正確性是沒有影響的,只是減慢了一部分查詢的響應時間。布隆過濾器的實現對刪除元素不太支援,雖然有些衍生的版本實現了這一功能,這裡不再討論。對於上述這種情景,我們可以結合實際的業務資料測量,來定期的重建布隆過濾器來解決。
2、移除入庫查詢
其實,對於我們上述的這種快取應用場景,我們為什麼要入庫查詢呢?
因為快取過期。
按照我們通常的認知,快取好像都必須要有過期時間,一來是為了保障資料一致性(快取資料與庫資料),同時也是為了有效的控制快取成本(熱點資料存留)。
但是,像我們列舉的這種量比不均的場景,就不可避免的引發穿透這種更加具有危害性的結果。
因此,此處,我們需要換一種方式來應用快取。
我們可以將已有的關係資料全部快取起來,設定不過期,同時移除後續的入庫查詢邏輯,所有的查詢全部由快取資料來響應。
同時為了保障資料一致性,我們可以對資料變更做補償措施。
資料的變更畢竟很少,相對於查詢畢竟有量級的差距。
因此,每次資料變更我們都可以進行一次資料同步(快取和資料庫),當然,方式可以選擇同步或者非同步。