MyBatis10-二級(jí)緩存

1. 二級(jí)緩存的原理

前面介紹了,mybatis中的二級(jí)緩存是mapper級(jí)別的緩存,值得注意的是,不同的mapper都有一個(gè)二級(jí)緩存,也就是說,不同的mapper之間的二級(jí)緩存是互不影響的。為了更加清楚的描述二級(jí)緩存,先來看一個(gè)示意圖:



從圖中可以看出:

1.sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會(huì)將查詢數(shù)據(jù)存儲(chǔ)到該UserMapper的二級(jí)緩存中。
2.如果SqlSession3去執(zhí)行相同 mapper下sql,執(zhí)行commit提交,則會(huì)清空該UserMapper下二級(jí)緩存區(qū)域的數(shù)據(jù)。
3.sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數(shù)據(jù),如果存在直接從緩存中取出數(shù)據(jù)。
緩存的執(zhí)行原理和前面提到的一級(jí)緩存是差不多的,二級(jí)緩存與一級(jí)緩存區(qū)別在于二級(jí)緩存的范圍更大,多個(gè)sqlSession可以共享一個(gè)mapper中的二級(jí)緩存區(qū)域。mybatis是如何區(qū)分不同mapper的二級(jí)緩存區(qū)域呢?它是按照不同mapper有不同的namespace來區(qū)分的,也就是說,如果兩個(gè)mapper的namespace相同,即使是兩個(gè)mapper,那么這兩個(gè)mapper中執(zhí)行sql查詢到的數(shù)據(jù)也將存在相同的二級(jí)緩存區(qū)域中。

2. 二級(jí)緩存的使用

明白了mybatis中二級(jí)緩存的原理后,接下來就是如何使用二級(jí)緩存了。在使用之前,首先得開啟二級(jí)緩存的開關(guān)。

2.1 開啟二級(jí)緩存

由于mybaits的二級(jí)緩存是mapper范圍級(jí)別,所以除了在SqlMapConfig.xml設(shè)置二級(jí)緩存的總開關(guān)外,還要在具體的mapper.xml中開啟二級(jí)緩存。設(shè)置如下:



這是在SqlMapConfig.xml中設(shè)置的,還得在具體的mapper.xml中設(shè)置,如下:


可以看到,具體的mapper中僅僅就一個(gè)<cache>標(biāo)簽,并沒有配置啥東西,這是因?yàn)閙ybatis中有默認(rèn)的實(shí)現(xiàn),我們?nèi)绻慌渲茫敲淳湍J(rèn)使用那個(gè)默認(rèn)的實(shí)現(xiàn)。在mybatis的核心包里有cache的接口和這個(gè)默認(rèn)的實(shí)現(xiàn),我截個(gè)圖:


所以就明白了,為啥不用配置都可以使用,mybatis中也就只有這一個(gè)默認(rèn)實(shí)現(xiàn)類,如果不使用mybatis的默認(rèn)二級(jí)緩存的話,就需要自己實(shí)現(xiàn)cache接口,然后再在mapper.xml中配置一下了,關(guān)于這個(gè)我在下面再談,現(xiàn)在先把二級(jí)緩存用起來!

將po類實(shí)現(xiàn)Serializable接口

開啟了二級(jí)緩存后,還需要將要緩存的pojo實(shí)現(xiàn)Serializable接口,為了將緩存數(shù)據(jù)取出執(zhí)行反序列化操作,因?yàn)槎?jí)緩存數(shù)據(jù)存儲(chǔ)介質(zhì)多種多樣,不一定只存在內(nèi)存中,有可能存在硬盤中,如果我們要再取這個(gè)緩存的話,就需要反序列化了。所以建議mybatis中的pojo都去實(shí)現(xiàn)Serializable接口。下面以User為例截個(gè)圖:


2.3 測(cè)試mybatis的二級(jí)緩存

@Test
public void testCache2() throws Exception {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();
    // 創(chuàng)建代理對(duì)象
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    // 第一次發(fā)起請(qǐng)求,查詢id為1的用戶
    User user1 = userMapper1.findUserById(1);
    System.out.println(user1);  
    //這里執(zhí)行關(guān)閉操作,將sqlsession中的數(shù)據(jù)寫到二級(jí)緩存區(qū)域
    sqlSession1.close();

    //sqlSession3用來清空緩存的,如果要測(cè)試二級(jí)緩存,需要把該部分注釋掉
    //使用sqlSession3執(zhí)行commit()操作
    UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    User user  = userMapper3.findUserById(1);
    user.setUsername("倪升武");
    userMapper3.updateUser(user);
    //執(zhí)行提交,清空UserMapper下邊的二級(jí)緩存
    sqlSession3.commit();
    sqlSession3.close();

    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    // 第二次發(fā)起請(qǐng)求,查詢id為1的用戶
    User user2 = userMapper2.findUserById(1);
    System.out.println(user2);

    sqlSession2.close();

}

我們先把sqlSession3部分注釋掉來測(cè)試一下二級(jí)緩存的結(jié)果:

二級(jí)緩存
  當(dāng)我們把sqlSession3部分加上后,再測(cè)試一下二級(jí)緩存結(jié)果:
二級(jí)緩存2
  到這里,就明白了mybatis中二級(jí)緩存的執(zhí)行原理了.

2.4 其他配置(useCache和flushCache)

mybatis中還可以配置userCache和flushCache等配置項(xiàng),userCache是用來設(shè)置是否禁用二級(jí)緩存的,在statement中設(shè)置useCache=false可以禁用當(dāng)前select語句的二級(jí)緩存,即每次查詢都會(huì)發(fā)出sql去查詢,默認(rèn)情況是true,即該sql使用二級(jí)緩存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

這種情況是針對(duì)每次查詢都需要最新的數(shù)據(jù)sql,要設(shè)置成useCache=false,禁用二級(jí)緩存,直接從數(shù)據(jù)庫中獲取。 在mapper的同一個(gè)namespace中,如果有其它insert、update、delete操作數(shù)據(jù)后需要刷新緩存,如果不執(zhí)行刷新緩存會(huì)出現(xiàn)臟讀。 設(shè)置statement配置中的flushCache=”true” 屬性,默認(rèn)情況下為true,即刷新緩存,如果改成false則不會(huì)刷新。使用緩存時(shí)如果手動(dòng)修改數(shù)據(jù)庫表中的查詢數(shù)據(jù)會(huì)出現(xiàn)臟讀。

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

一般下執(zhí)行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存,這樣可以避免數(shù)據(jù)庫臟讀。所以我們不用設(shè)置,默認(rèn)即可,這里只是提一下.

3. MyBatis整合ehcache分布式緩存框架

3.1 問題的由來

上面的部分主要總結(jié)了一下mybatis中二級(jí)緩存的使用,但是mybatis中默認(rèn)自帶的二級(jí)緩存有個(gè)弊端,即無法實(shí)現(xiàn)分布式緩存,什么意思呢?就是說緩存的數(shù)據(jù)在自己的服務(wù)器上,假設(shè)現(xiàn)在有兩個(gè)服務(wù)器A和B,用戶訪問的時(shí)候訪問了A服務(wù)器,查詢后的緩存就會(huì)放在A服務(wù)器上,假設(shè)現(xiàn)在有個(gè)用戶訪問的是B服務(wù)器,那么他在B服務(wù)器上就無法獲取剛剛那個(gè)緩存,如下圖所示:
緩存弊端

  所以我們?yōu)榱私鉀Q這個(gè)問題,就得找一個(gè)分布式的緩存,專門用來存儲(chǔ)緩存數(shù)據(jù)的,這樣不同的服務(wù)器要緩存數(shù)據(jù)都往它那里存,取緩存數(shù)據(jù)也從它那里取,如下圖所示:
分布式緩存
  這樣就能解決上面所說的問題,為了提高系統(tǒng)并發(fā)性能、我們一般對(duì)系統(tǒng)進(jìn)行上面這種分布式部署(集群部署方式),所以要使用分布式緩存對(duì)緩存數(shù)據(jù)進(jìn)行集中管理。但是mybatis無法實(shí)現(xiàn)分布式緩存,需要和其它分布式緩存框架進(jìn)行整合,這里主要介紹ehcache。

3.2 整合方法

上文一開始提到過,mybatis提供了一個(gè)cache接口,如果要實(shí)現(xiàn)自己的緩存邏輯,實(shí)現(xiàn)cache接口開發(fā)即可。mybatis本身默認(rèn)實(shí)現(xiàn)了一個(gè),但是這個(gè)緩存的實(shí)現(xiàn)無法實(shí)現(xiàn)分布式緩存,所以我們要自己來實(shí)現(xiàn)。ehcache分布式緩存就可以,mybatis提供了一個(gè)針對(duì)cache接口的ehcache實(shí)現(xiàn)類,這個(gè)類在mybatis和ehcache的整合包中。所以首先我們需要導(dǎo)入整合包(點(diǎn)我下載)。

jar包
  導(dǎo)入了jar包后,配置mapper中cache中的type為ehcache對(duì)cache接口的實(shí)現(xiàn)類型。ehcache對(duì)cache接口有一個(gè)實(shí)現(xiàn)類為:
實(shí)現(xiàn)類
  我們將該類的完全限定名寫到type屬性中即可,如下:
配置
  OK,配置完成,現(xiàn)在mybatis就會(huì)自動(dòng)去執(zhí)行這個(gè)ehcache實(shí)現(xiàn)類了,就不會(huì)使用自己默認(rèn)的二級(jí)緩存了,但是使用ehcache還有一個(gè)緩存配置別忘了,在classpath下新建一個(gè)ehcache.xml文件:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="F:\develop\ehcache"/>

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
</ehcache>

4. 二級(jí)緩存的應(yīng)用場(chǎng)景和局限性

對(duì)于訪問多的查詢請(qǐng)求且用戶對(duì)查詢結(jié)果實(shí)時(shí)性要求不高,此時(shí)可采用mybatis二級(jí)緩存技術(shù)降低數(shù)據(jù)庫訪問量,提高訪問速度,業(yè)務(wù)場(chǎng)景比如:耗時(shí)較高的統(tǒng)計(jì)分析sql、電話賬單查詢sql等。實(shí)現(xiàn)方法如下:通過設(shè)置刷新間隔時(shí)間,由mybatis每隔一段時(shí)間自動(dòng)清空緩存,根據(jù)數(shù)據(jù)變化頻率設(shè)置緩存刷新間隔flushInterval,比如設(shè)置為30分鐘、60分鐘、24小時(shí)等,根據(jù)需求而定。
  mybatis二級(jí)緩存對(duì)細(xì)粒度的數(shù)據(jù)級(jí)別的緩存實(shí)現(xiàn)不好,比如如下需求:對(duì)商品信息進(jìn)行緩存,由于商品信息查詢?cè)L問量大,但是要求用戶每次都能查詢最新的商品信息,此時(shí)如果使用mybatis的二級(jí)緩存就無法實(shí)現(xiàn)當(dāng)一個(gè)商品變化時(shí)只刷新該商品的緩存信息而不刷新其它商品的信息,因?yàn)閙ybaits的二級(jí)緩存區(qū)域以mapper為單位劃分的,當(dāng)一個(gè)商品信息變化會(huì)將所有商品信息的緩存數(shù)據(jù)全部清空。解決此類問題可能需要在業(yè)務(wù)層根據(jù)需求對(duì)數(shù)據(jù)有針對(duì)性緩存。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容