快取使用中的注意事項

隨風而逝,只是飄零發表於2016-12-09

一、快取穿透

快取穿透是指查詢一個一定不存在的資料,由於快取是不命中時被動寫的,並且出於容錯考慮,如果從儲存層查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到儲存層去查詢,失去了快取的意義。

黑客向目標系統查詢一個必然不存在的資料。有可能是資料真的不存在,也有可能是第三方惡意攻擊系統,刻意構建了大量不存在的id來攻擊資料庫。如果短時間內極大量的出現快取穿透,那麼系統的資料庫將面臨極大的壓力,甚至當機。

有很多種方法可以有效地解決快取穿透問題,最常見的則是採用布隆過濾器,將所有可能存在的資料雜湊到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力。在資料魔方里,我們採用了一個更為簡單粗暴的方法,如果一個查詢返回的資料為空(不管是資料不存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。

解決方案:

(1) 儲存空值:對不存在的值也在快取中儲存一個特殊的值表示Empty,併為這個id設定一個比較短的快取過期的時間,這樣,這個空的快取失效之後還能到資料庫查詢這個id的對應的值是不是已經存在。

二、快取併發

有時候如果網站併發訪問高,一個快取如果失效,可能出現多個程式同時查詢DB,同時設定快取的情況,如果併發確實很大,這也可能造成DB壓力過大,還有快取頻繁更新的問題。

我現在的想法是再APP中對快取查詢加鎖,如果KEY不存在,就加鎖,然後查DB入快取,然後解鎖;其他程式如果發現有鎖就等待,然後等解鎖後返回資料或者進入DB查詢。

解決方案:

在某個請求發現快取沒有資料時,此時需要獲取一個排它鎖,由此請求查詢資料庫並更新快取。此步驟完成後釋放此鎖。在此請求操作的過程中,所有其他請求同一id的請求阻塞,待這個更新請求完成後直接從快取重取資料。

三、快取失效

引起這個問題的主要原因還是高併發的時候,平時我們設定一個快取的過期時間時,可能有一些會設定5分鐘啊,10分鐘這些;併發很高時可能會出在某一個時間同時生成了很多的快取,並且過期時間都一樣,這個時候就可能引發一當過期時間到後,這些快取同時失效,請求全部轉發到DB,DB可能會壓力過重。在系統啟動批量裝載資料快取時,快取的過期時間設定得過於一致;或者是在系統訪問的高峰期更新或載入了一大批快取,過期時間一致。快取系統出現問題重啟或者當機了。

其中的一個簡單方案就時講快取失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個快取的過期時間的重複率就會降低,就很難引發集體失效的事件。

解決方案:

(1)快取過期時間儘量散開,可以設定一個範圍,比如1-200秒,把快取的過期時間通過hash雜湊在這個區間,防止同一時間快取大量過期。

(2)快取雙寫策略,將同一個快取同時寫到兩個快取系統中。memcache有一個引數-b就是可以用來設定backup的快取,當主快取失效時還可以通過備份取到資料。

(3)快取永不過期,在快取系統中不設定過期時間,所有的快取資料的更新都通過業務邏輯程式來完成。

總結:

1、快取穿透:查詢一個必然不存在的資料。比如文章表,查詢一個不存在的id,每次都會訪問DB,如果有人惡意破壞,很可能直接對DB造成影響。

2、快取失效:如果快取集中在一段時間內失效,DB的壓力凸顯。這個沒有完美解決辦法,但可以分析使用者行為,儘量讓失效時間點均勻分佈。

當發生大量的快取穿透,例如對某個失效的快取的大併發訪問就造成了快取雪崩。

四、多級快取

將ehcache與redis做二級快取

Hibernate中的二級快取,二級快取是屬於SessionFactory級別的快取機制。第一級別的快取是Session級別的快取,是屬於事務範圍的快取,由Hibernate管理,一般無需進行干預。第二級別的快取是SessionFactory級別的快取,是屬於程式範圍的快取。

MyBatis的一級快取指的是在一個Session域內,session為關閉的時候執行的查詢會根據SQL為key被快取(跟mysql快取一樣,修改任何引數的值都會導致快取失效)

mybatis的二級快取,二級快取是mapper級別的快取,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級快取,二級快取是跨SqlSession的。

mybaits的二級快取是mapper範圍級別,除了在SqlMapConfig.xml設定二級快取的總開關,還要在具體的mapper.xml中開啟二級快取。

在核心配置檔案SqlMapConfig.xml中加入<setting name="cacheEnabled" value="true"/>

一級快取是SqlSession級別的快取。在運算元據庫時需要構造 sqlSession物件,在物件中有一個(記憶體區域)資料結構(HashMap)用於儲存快取資料。不同的sqlSession之間的快取資料區域(HashMap)是互相不影響的。

一級快取的作用域是同一個SqlSession,在同一個sqlSession中兩次執行相同的sql語句,第一次執行完畢會將資料庫中查詢的資料寫到快取(記憶體),第二次會從快取中獲取資料將不再從資料庫查詢,從而提高查詢效率。當一個sqlSession結束後該sqlSession中的一級快取也就不存在了。Mybatis預設開啟一級快取。

二級快取是mapper級別的快取,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession去運算元據庫得到資料會存在二級快取區域,多個SqlSession可以共用二級快取,二級快取是跨SqlSession的。

二級快取是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執行相同namespace下的sql語句且向sql中傳遞引數也相同即最終執行相同的sql語句,第一次執行完畢會將資料庫中查詢的資料寫到快取(記憶體),第二次會從快取中獲取資料將不再從資料庫查詢,從而提高查詢效率。Mybatis預設沒有開啟二級快取需要在setting全域性引數中配置開啟二級快取。

適合放入二級快取中資料

很少被修改

不是很重要的資料,允許出現偶爾的併發問題

不適合放入二級快取中的資料

經常被修改

財務資料,絕對不允許出現併發問題

與其他應用資料共享的資料

相關文章