Mybatis緩存,一級緩存,二級緩存,緩存穿透、緩存擊穿、緩存雪崩區別和解決方案

一級緩存

  Mybatis對緩存提供支持,但是在沒有配置的默認情況下,它只開啓一級緩存,一級緩存只是相對於同一個SqlSession而言。所以在參數和SQL完全一樣的情況下,我們使用同一個SqlSession對象調用一個Mapper方法,往往只執行一次SQL,因爲使用SelSession第一次查詢後,MyBatis會將其放在緩存中,以後再查詢的時候,如果沒有聲明需要刷新,並且緩存沒有超時的情況下,SqlSession都會取出當前緩存的數據,而不會再次發送SQL到數據庫。

 爲什麼要使用一級緩存,不用多說也知道個大概。但是還有幾個問題我們要注意一下。

  1、一級緩存的生命週期有多長?

  a、MyBatis在開啓一個數據庫會話時,會 創建一個新的SqlSession對象,SqlSession對象中會有一個新的Executor對象。Executor對象中持有一個新的PerpetualCache對象;當會話結束時,SqlSession對象及其內部的Executor對象還有PerpetualCache對象也一併釋放掉。

  b、如果SqlSession調用了close()方法,會釋放掉一級緩存PerpetualCache對象,一級緩存將不可用。

  c、如果SqlSession調用了clearCache(),會清空PerpetualCache對象中的數據,但是該對象仍可使用。

  d、SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache對象的數據,但是該對象可以繼續使用

    2、怎麼判斷某兩次查詢是完全相同的查詢?

  mybatis認爲,對於兩次查詢,如果以下條件都完全一樣,那麼就認爲它們是完全相同的兩次查詢。

  2.1 傳入的statementId

  2.2 查詢時要求的結果集中的結果範圍

  2.3. 這次查詢所產生的最終要傳遞給JDBC java.sql.Preparedstatement的Sql語句字符串(boundSql.getSql() )

  2.4 傳遞給java.sql.Statement要設置的參數值

二級緩存:

  MyBatis的二級緩存是Application級別的緩存,它可以提高對數據庫查詢的效率,以提高應用的性能。

  MyBatis的緩存機制整體設計以及二級緩存的工作模式

 

SqlSessionFactory層面上的二級緩存默認是不開啓的,二級緩存的開席需要進行配置,實現二級緩存的時候,MyBatis要求返回的POJO必須是可序列化的。 也就是要求實現Serializable接口,配置方法很簡單,只需要在映射XML文件配置就可以開啓緩存了<cache/>,如果我們配置了二級緩存就意味着:

  • 映射語句文件中的所有select語句將會被緩存。
  • 映射語句文件中的所欲insert、update和delete語句會刷新緩存。
  • 緩存會使用默認的Least Recently Used(LRU,最近最少使用的)算法來收回。
  • 根據時間表,比如No Flush Interval,(CNFI沒有刷新間隔),緩存不會以任何時間順序來刷新。
  • 緩存會存儲列表集合或對象(無論查詢方法返回什麼)的1024個引用
  • 緩存會被視爲是read/write(可讀/可寫)的緩存,意味着對象檢索不是共享的,而且可以安全的被調用者修改,不干擾其他調用者或線程所做的潛在修改。

實踐:

創建一個POJO Bean並序列化

  由於二級緩存的數據不一定都是存儲到內存中,它的存儲介質多種多樣,所以需要給緩存的對象執行序列化。(如果存儲在內存中的話,實測不序列化也可以的。)

在 mybatis-config.xml中開啓二級緩存

複製代碼
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!--這個配置使全局的映射器(二級緩存)啓用或禁用緩存-->
        <setting name="cacheEnabled" value="true" />
        .....
    </settings>
    ....
</configuration>

Mapper配置 

哪個mapper用在哪個mapper配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "http://mybatis.org/dtd/mybatis-3-mapper.dtd" "mybatis-3-mapper.dtd" >
<mapper namespace="com.baizhi.zcn.dao.BookDao" >
	
	    <!--開啓本mapper的namespace下的二級緩存-->
    <!--
        eviction:代表的是緩存回收策略,目前MyBatis提供以下策略。
        (1) LRU,最近最少使用的,一處最長時間不用的對象
        (2) FIFO,先進先出,按對象進入緩存的順序來移除他們
        (3) SOFT,軟引用,移除基於垃圾回收器狀態和軟引用規則的對象
        (4) WEAK,弱引用,更積極的移除基於垃圾收集器狀態和弱引用規則的對象。這裏採用的是LRU,
                移除最長時間不用的對形象

        flushInterval:刷新間隔時間,單位爲毫秒,這裏配置的是100秒刷新,如果你不配置它,那麼當
        SQL被執行的時候纔會去刷新緩存。

        size:引用數目,一個正整數,代表緩存最多可以存儲多少個對象,不宜設置過大。設置過大會導致內存溢出。
        這裏配置的是1024個對象

        readOnly:只讀,意味着緩存數據只能讀取而不能修改,這樣設置的好處是我們可以快速讀取緩存,缺點是我們沒有
        辦法修改緩存,他的默認值是false,不允許我們修改
    -->
    <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
	...
</mapper>

注意:實體類要實現序列化接口

implements Serializable

一、緩存處理流程

      前臺請求,後臺先從緩存中取數據,取到直接返回結果,取不到時從數據庫中取,數據庫取到更新緩存,並返回結果,數據庫也沒取到,那直接返回空結果。

二、緩存穿透

       描述:

       緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷髮起請求,如發起爲id爲“-1”的數據或id爲特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。

緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時被動寫的,並且出於容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。

      解決方案:

接口層增加校驗,如用戶鑑權校驗,id做基礎校驗,id<=0的直接攔截;
從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫爲key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反覆用同一個id暴力攻擊


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

三、緩存擊穿

      描述:

      緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由於併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力

      解決方案:

設置熱點數據永遠不過期。
加互斥鎖,互斥鎖

對於一些設置了過期時間的key,如果這些key可能會在某些時間點被超高併發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題,這個和緩存雪崩的區別在於這裏針對某一key緩存,前者則是很多key。

緩存在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的併發請求過來,這些請求發現緩存過期一般都會從後端DB加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮。

四、緩存雪崩

      描述:

      緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是, 緩存擊穿指併發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。

緩存雪崩是指在我們設置緩存時採用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩。

     解決方案:

緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
如果緩存數據庫是分佈式部署,將熱點數據均勻分佈在不同搞得緩存數據庫中。
設置熱點數據永遠不過期。

緩存失效時的雪崩效應對底層系統的衝擊非常可怕。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。這裏分享一個簡單方案就時講緩存失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章