緩存實現的方式
-
一級緩存java
- 二級緩存
案例實操
1. 一級緩存
基於 PerpetualCache 的 HashMap 本地緩存(mybatis 內部實現 cache 接口),其存儲做用域爲 Session,當 Session flush 或 close 以後,該 Session 中的全部 Cache 就將清空;redis
2. 二級緩存
一級緩存其機制相同,默認也是採用 PerpetualCache 的 HashMap 存儲,不一樣在於其存儲做用域爲 Mapper(Namespace),而且可自定義存儲源,如 Ehcache;算法
對於緩存數據更新機制,當某一個做用域(一級緩存 Session/二級緩存 Namespaces)的進行了 C/R/U/D 操做後,默認該做用域下全部 select 中的緩存將被 clear。sql
若是二緩存開啓,首先從二級緩存查詢數據,若是二級緩存有則從二級緩存中獲取數據,若是二級緩存沒有,從一級緩存找是否有緩存數據,若是一級緩存沒有,查詢數據庫。數據庫
3. 二級緩存侷限性
mybatis 二級緩存對細粒度的數據級別的緩存實現很差,對同時緩存較多條數據的緩存,好比以下需求:對商品信息進行緩存,因爲商品信息查詢訪問量大,可是要求用戶每次都能查詢最新的商品信息,此時若是使用 mybatis 的二級緩存就沒法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,由於 mybaits 的二級緩存區域以 mapper 爲單位劃分,當一個商品信息變化會將全部商品信息的緩存數據所有清空 api
4. 一級緩存(默認開啓)
Mybatis 默認提供一級緩存,緩存範圍是一個 sqlSession。在同一個 SqlSession 中,兩次執行相同的 sql 查詢,第二次再也不從數據庫查詢。緩存
原理:一級緩存採用 Hashmap 存儲,mybatis 執行查詢時,從緩存中查詢,若是緩存中沒有從數據庫查詢。若是該 SqlSession 執行 clearCache() 提交或者增長刪除修改操做,清除緩存。服務器
默認就存在,瞭解觀察結果便可session
a.緩存存在狀況(session 未提交)
@Test public void test01() { SqlSession sqlSession=sqlSessionFactory.openSession(); AccountDao accountDao=sqlSession.getMapper(AccountDao.class); Account account=accountDao.queryAccountById(1); System.out.println(account); accountDao.queryAccountById(1); }
日誌僅打印一條 sql mybatis
b.刷新緩存
Session 提交此時緩存數據被刷新
@Test public void test02() { SqlSession sqlSession=sqlSessionFactory.openSession(); AccountDao accountDao=sqlSession.getMapper(AccountDao.class); Account account=accountDao.queryAccountById(1); System.out.println(account); sqlSession.clearCache(); accountDao.queryAccountById(1); }
效果:
5. 二級緩存
一級緩存是在同一個 sqlSession 中,二級緩存是在同一個 namespace 中,所以相同的 namespace 不一樣的 sqlsession 可使用二級緩存。
使用場景
- 對查詢頻率高,變化頻率低的數據建議使用二級緩存。
- 對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可採用 mybatis 二級緩存技術下降數據庫訪問量,提升訪問速度,業務場景好比:耗時較高的統計分析 sql、電話帳單查詢 sql 等。
全局文件配置(mybatis.xml)
<setting name="cacheEnabled" value="true"/>
Mapper.xml 中加入 :打開該 mapper 的二級緩存 <!-- 開啓該 mapper 的二級緩存 --> <cache/>
cache 標籤經常使用屬性
<cache eviction="FIFO" <!--回收策略爲先進先出--> flushInterval="60000" <!--自動刷新時間 60s--> size="512" <!--最多緩存 512 個引用對象--> readOnly="true"/> <!--只讀-->
說明:
- 映射語句文件中的全部 select 語句將會被緩存。
- 映射語句文件中的全部 insert,update 和 delete 語句會刷新緩存。
- 緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
- 緩存會根據指定的時間間隔來刷新.
- 緩存會存儲 1024 個對象
PO 對象必須支持序列化
public class User implements Serializable { }
關閉 Mapper 下的具體的 statement 的緩存
使用 useCache:默認爲 true
<select id="findUserByid" parameterType="int" resultType="User" useCache="false"> SELECT * FROM user WHERE id=#{id} </select>
刷新二級緩存
操做 CUD 的 statement 時候,會強制刷新二級緩存 即默認 flushCache="true" ,若是想關閉設定爲 flushCache="false"便可 ,不建議關閉刷新,由於操做更新刪除修改,關閉後容易獲取髒數據。
二級緩存測試:
@Test public void test03() { SqlSession sqlSession=sqlSessionFactory.openSession(); AccountDao accountDao=sqlSession.getMapper(AccountDao.class); Account account=accountDao.queryAccountById(1); System.out.println(account); sqlSession.close(); SqlSession sqlSession2=sqlSessionFactory.openSession(); AccountDao accountDao2=sqlSession2.getMapper(AccountDao.class); accountDao2.queryAccountById(1); sqlSession.close(); }
效果:
擴展
分佈式緩存 ehcache
若是有多條服務器 ,不使用分佈緩存,緩存的數據在各個服務器單獨存儲,不方便系統開發。因此要使用分佈式緩存對緩存數據進行集中管理。所以但是使用 ehcache memcached redis
mybatis 自己來講是沒法實現分佈式緩存的,因此要與分佈式緩存框架進行整合。 EhCache 是一個純 Java 的進程內緩存框架,具備快速、精幹等特色;Ehcache 是一種普遍 使用的開源 Java 分佈式緩存。主要面向通用緩存,Java EE 和輕量級容器。它具備內存和磁盤存儲,緩存加載器,緩存擴展,緩存異常處理程序,一個 gzip 緩存 servlet 過濾器,支持 REST 和 SOAP api 等特色。
Jar 依賴
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.3</version> </dependency>
緩存接口配置
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
在 src 下 加入 ehcache.xml(不是必須的沒有使用默認配置)
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../bin/ehcache.xsd"> <!-- name:Cache 的惟一標識 maxElementsInMemory:內存中最大緩存對象數 maxElementsOnDisk:磁盤中最大緩存對象數,如果 0 表示無窮大 eternal:Element 是否永遠不過時,若是爲 true,則緩存的數據始終有效,若是爲 false 那麼還要根據 timeToIdleSeconds,timeToLiveSeconds 判斷 overflowToDisk:配置此屬性,當內存中 Element 數量達到 maxElementsInMemory 時, Ehcache 將會 Element 寫到磁盤中 timeToIdleSeconds:設置 Element 在失效前的容許閒置時間。僅當 element 不是永久有效 時使用,可選屬性,默認值是 0,也就是可閒置時間無窮大 timeToLiveSeconds:設置 Element 在失效前容許存活時間。最大時間介於建立時間和失效 時間之間。僅當 element 不是永久有效時使用,默認是 0.,也就是 element 存活時間無窮 大 diskPersistent:是否緩存虛擬機重啓期數據 diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是 120 秒 diskSpoolBufferSizeMB:這個參數設置 DiskStore(磁盤緩存)的緩存區大小。默認是 30MB。每一個 Cache 都應該有本身的一個緩衝區 memoryStoreEvictionPolicy:當達到 maxElementsInMemory 限制時,Ehcache 將會根據 指定的策略去清理內存。默認策略是 LRU(最近最少使用)。你能夠設置爲 FIFO(先進先 出)或是 LFU(較少使用) --> <defaultCache overflowToDisk="true" eternal="false"/> <diskStore path="D:/cache" /> <!-- <cache name="sxtcache" overflowToDisk="true" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" maxElementsInMemory="1000" maxElementsOnDisk="10" diskPersistent="true" diskExpiryThreadIntervalSeconds="300" diskSpoolBufferSizeMB="100" memoryStoreEvictionPolicy="LRU" /> -->
測試:
@Test public void test04() { SqlSession sqlSession=sqlSessionFactory.openSession(); AccountDao accountDao=sqlSession.getMapper(AccountDao.class); Account account=accountDao.queryAccountById(1); System.out.println(account); sqlSession.close(); SqlSession sqlSession2=sqlSessionFactory.openSession(); AccountDao accountDao2=sqlSession2.getMapper(AccountDao.class); accountDao2.queryAccountById(1); sqlSession.close(); }
效果: Cache Hit Ratio [com.xxx.dao.AccountDao]:0.5