1. 程式人生 > >myBatis-一級緩存與二級緩存

myBatis-一級緩存與二級緩存

沒有 速度 myba 狀態 將不 order tor mit 修改

1.1 什麽是查詢緩存

mybatis提供查詢緩存,用於減輕數據壓力,提高數據庫性能。

mybaits提供一級緩存,和二級緩存。

一級緩存是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全局參數中配置開啟二級緩存。

如果緩存中有數據就不用從數據庫中獲取,大大提高系統性能。

1.2 一級緩存

1.2.1 一級緩存工作原理

下圖是根據id查詢用戶的一級緩存圖解

技術分享

第一次發起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,如果沒有,從數據庫查詢用戶信息。

得到用戶信息,將用戶信息存儲到一級緩存中。

如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。

第二次發起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。

1.2.2 一級緩存測試

mybatis默認支持一級緩存,不需要在配置文件去配置。

按照上邊一級緩存原理步驟去測試。

@Test

public void testCache1() throws Exception{

SqlSessionsqlSession = sqlSessionFactory.openSession();//創建代理對象

UserMapperuserMapper = sqlSession.getMapper(UserMapper.class);

//下邊查詢使用一個SqlSession

//第一次發起請求,查詢id為1的用戶

Useruser1 = userMapper.findUserById(1);

System.out.println(user1);

// 如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。

//更新user1的信息

user1.setUsername(‘測試用戶22‘);

userMapper.updateUser(user1);

//執行commit操作去清空緩存

sqlSession.commit();

//第二次發起請求,查詢id為1的用戶

Useruser2 = userMapper.findUserById(1);

System.out.println(user2);

sqlSession.close();

}

1.2.3 一級緩存應用

正式開發,是將mybatis和spring進行整合開發,事務控制在service中。

一個service方法中包括很多mapper方法調用。

service{

//開始執行時,開啟事務,創建SqlSession對象

//第一次調用mapper的方法findUserById(1)

//第二次調用mapper的方法findUserById(1),從一級緩存中取數據

//aop控制 只要方法結束,sqlSession關閉 sqlsession關閉後就銷毀數據結構,清空緩存

Service結束sqlsession關閉

}

如果是執行兩次service調用查詢相同的用戶信息,不走一級緩存,因為Service方法結束,sqlSession就關閉,一級緩存就清空。

1.3 二級緩存

1.3.1 原理

首先開啟mybatis的二級緩存。

sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。

如果SqlSession3去執行相同 mapper下sql,執行commit提交,清空該 mapper下的二級緩存區域的數據。

sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數據,如果存在直接從緩存中取出數據。

二級緩存與一級緩存區別,二級緩存的範圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區域。數據類型仍然為HashMap

UserMapper有一個二級緩存區域(按namespace分,如果namespace相同則使用同一個相同的二級緩存區),其它mapper也有自己的二級緩存區域(按namespace分)。

每一個namespace的mapper都有一個二緩存區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到數據將存在相同的二級緩存區域中。

1.3.2 開啟二級緩存

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

在核心配置文件SqlMapConfig.xml中加入

<settingname=‘cacheEnabled‘value=‘true‘/>

<!-- 全局配置參數,需要時再設置 -->

<settings>

<!-- 開啟二級緩存 默認值為true -->

<settingname=‘cacheEnabled‘value=‘true‘/>

</settings>

描述

允許值

默認值

cacheEnabled

對在此配置文件下的所有cache 進行全局性開/關設置。

true false

true

在UserMapper.xml中開啟二緩存,UserMapper.xml下的sql執行完成會存儲到它的緩存區域(HashMap)。

<mappernamespace=‘cn.hpu.mybatis.mapper.UserMapper‘>

<!-- 開啟本mappernamespace下的二級緩存 -->

<cache></cache>

1.3.3 調用pojo類實現序列化接口

public class Userimplements Serializable {

//Serializable實現序列化,為了將來反序列化

二級緩存需要查詢結果映射的pojo對象實現java.io.Serializable接口實現序列化和反序列化操作,註意如果存在父類、成員pojo都需要實現序列化接口。

pojo類實現序列化接口是為了將緩存數據取出執行反序列化操作,因為二級緩存數據存儲介質多種多樣,不一定在內存有可能是硬盤或者遠程服務器。

1.3.4 測試方法

// 二級緩存測試

@Test

public void testCache2() throws Exception {

SqlSessionsqlSession1 = sqlSessionFactory.openSession();

SqlSessionsqlSession2 = sqlSessionFactory.openSession();

SqlSessionsqlSession3 = sqlSessionFactory.openSession();

// 創建代理對象

UserMapperuserMapper1 = sqlSession1.getMapper(UserMapper.class);

// 第一次發起請求,查詢id為1的用戶

Useruser1 = userMapper1.findUserById(1);

System.out.println(user1);

//這裏執行關閉操作,將sqlsession中的數據寫到二級緩存區域

sqlSession1.close();

//使用sqlSession3執行commit()操作

UserMapperuserMapper3 = sqlSession3.getMapper(UserMapper.class);

Useruser = userMapper3.findUserById(1);

user.setUsername(‘張明明‘);

userMapper3.updateUser(user);

//執行提交,清空UserMapper下邊的二級緩存

sqlSession3.commit();

sqlSession3.close();

UserMapperuserMapper2 = sqlSession2.getMapper(UserMapper.class);

// 第二次發起請求,查詢id為1的用戶

Useruser2 = userMapper2.findUserById(1);

System.out.println(user2);

sqlSession2.close();

}

1.3.5 useCache配置禁用二級緩存

在statement中設置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。

<selectid=‘findOrderListResultMap‘ resultMap=‘ordersUserMap‘ useCache=‘false‘>

總結:針對每次查詢都需要最新的數據sql,要設置成useCache=false,禁用二級緩存。

1.3.6 mybatis刷新緩存(就是清空緩存)

在mapper的同一個namespace中,如果有其它insert、update、delete操作數據後需要刷新緩存,如果不執行刷新緩存會出現臟讀。

設置statement配置中的flushCache=‘true‘ 屬性,默認情況下為true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現臟讀。

如下:

<insertid=‘insertUser‘ parameterType=‘cn.itcast.mybatis.po.User‘ flushCache=‘true‘>

總結:一般下執行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存默認情況下為true,我們不用去設置它,這樣可以避免數據庫臟讀。

1.3.7 Mybatis Cache參數

flushInterval(刷新間隔)可以被設置為任意的正整數,而且它們代表一個合理的毫秒形式的時間段。默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。

size(引用數目)可以被設置為任意正整數,要記住你緩存的對象數目和你運行環境的可用內存資源數目。默認值是1024。

readOnly(只讀)屬性可以被設置為true或false。只讀的緩存會給所有調用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優勢。可讀寫的緩存會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是false。

如下例子:

<cache eviction=‘FIFO‘ flushInterval=‘60000‘ size=‘512‘ readOnly=‘true‘/>

這個更高級的配置創建了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此在不同線程中的調用者之間修改它們會導致沖突。可用的收回策略有, 默認的是 LRU:

1. LRU – 最近最少使用的:移除最長時間不被使用的對象。

2. FIFO – 先進先出:按對象進入緩存的順序來移除它們。

3. SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。

4. WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。

1.4 mybatis整合ehcache

ehcache是一個分布式緩存框架。

EhCache 是一個純Java的進程內緩存框架,是一種廣泛使用的開源Java分布式緩存,具有快速、精幹等特點,是Hibernate中默認的CacheProvider。

1.4.1 分布緩存

我們系統為了提高系統並發,性能、一般對系統進行分布式部署(集群部署方式)

技術分享

不使用分布緩存,緩存的數據在各各服務單獨存儲,不方便系統開發。所以要使用分布式緩存對緩存數據進行集中管理。

mybatis無法實現分布式緩存,需要和其它分布式緩存框架進行整合。

1.4.2 整合方法(掌握無論整合誰,首先想到改type接口)

mybatis提供了一個cache接口,如果要實現自己的緩存邏輯,實現cache接口開發即可。

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個cache接口的實現類。

1.4.3 第一步加入ehcache包

技術分享

1.4.4 整合ehcache

配置mapper中cache中的type為ehcache對cache接口的實現類型。

<mappernamespace=‘cn.hpu.mybatis.mapper.UserMapper‘>

<!-- 開啟本mappernamespace下的二級緩存

type:指定cache接口實現類,mybatis默認使用PerpetualCache

要和eache整合,需要配置type為ehcahe實現cache接口的類型

-->

<cachetype=‘org.mybatis.caches.ehcache.EhcacheCache‘>

</cache>

可以根據需求調整緩存參數:

<cachetype=‘org.mybatis.caches.ehcache.EhcacheCache‘>

<propertyname=‘timeToIdleSeconds‘value=‘3600‘/>

<propertyname=‘timeToLiveSeconds‘value=‘3600‘/>

<!-- 同ehcache參數maxElementsInMemory-->

<propertyname=‘maxEntriesLocalHeap‘value=‘1000‘/>

<!-- 同ehcache參數maxElementsOnDisk -->

<propertyname=‘maxEntriesLocalDisk‘value=‘10000000‘/>

<propertyname=‘memoryStoreEvictionPolicy‘value=‘LRU‘/>

</cache>

1.4.5 加入ehcache的配置文件

在classpath下配置ehcache.xml

<ehcachexmlns:xsi=‘http://www.w3.org/2001/XMLSchema-instance‘

xsi:noNamespaceSchemaLocation=‘../config/ehcache.xsd‘>

<diskStorepath=‘F:\develop\ehcache‘/>

<defaultCache

maxElementsInMemory=‘1000‘

maxElementsOnDisk=‘10000000‘

eternal=‘false‘

overflowToDisk=‘false‘

timeToIdleSeconds=‘120‘

timeToLiveSeconds=‘120‘

diskExpiryThreadIntervalSeconds=‘120‘

memoryStoreEvictionPolicy=‘LRU‘>

</defaultCache>

</ehcache>

屬性說明:

diskStore:指定數據在磁盤中的存儲位置。

defaultCache:當借助CacheManager.add(‘demoCache‘)創建Cache時,EhCache便會采用<defalutCache/>指定的的管理策略

以下屬性是必須的:

maxElementsInMemory - 在內存中緩存的element的最大數目

maxElementsOnDisk - 在磁盤上緩存的element的最大數目,若是0表示無窮大

eternal - 設定緩存的elements是否永遠不過期。如果為true,則緩存的數據始終有效,如果為false那麽還要根據timeToIdleSeconds,timeToLiveSeconds判斷

overflowToDisk- 設定當內存緩存溢出的時候是否將過期的element緩存到磁盤上

以下屬性是可選的:

timeToIdleSeconds - 當緩存在EhCache中的數據前後兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數據便會刪除,默認值是0,也就是可閑置時間無窮大

timeToLiveSeconds - 緩存element的有效生命期,默認是0.,也就是element存活時間無窮大

diskSpoolBufferSizeMB 這個參數設置DiskStore(磁盤緩存)的緩存區大小.默認是30MB.每個Cache都應該有自己的一個緩沖區.

diskPersistent在VM重啟的時候是否啟用磁盤保存EhCache中的數據,默認是false。

diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認是120秒。每個120s,相應的線程會進行一次EhCache中數據的清理工作

memoryStoreEvictionPolicy - 當內存緩存達到最大,有新的element加入的時候,移除緩存中element的策略。默認是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)

1.5 二級應用場景

對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。

實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鐘、60分鐘、24小時等,根據需求而定。

1.6 二級緩存局限性

mybatis二級緩存對細粒度的數據級別的緩存實現不好,對同時緩存較多條數據的緩存,比如如下需求:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區域以mapper為單位劃分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。需要使用三級緩存

myBatis-一級緩存與二級緩存