MyBatis快取實現原理總結概述
MyBatis提供兩種快取:一級快取【預設開啟的SqlSession級別的快取】和二級快取【Namespace Mapper級別的快取】。
1. SqlSession快取 存在SESSION【預設】和STATEMENT兩個選項,SESSION在MyBatis會話中執行的所有語句時共享這個快取,STATEMENT只快取對當前執行的這個Statement有效。SqlSession快取的作用域僅限當前SqlSession,SqlSession結束後會將快取Clear,在SqlSession中首次查詢資料時會將結果快取,在進行Update,Delete,Insert操作後會將快取清空。
在SqlSession是用Executor執行查詢操作時,會將結果儲存到Executor的快取PerpetualCache localCache中,原始碼如下所示: private <E> List<E> queryFromDatabase(查詢所需入參) throws SQLException { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); localCache.putObject(key, list); // 將查詢到的結果快取,當前key是CacheKey localOutputParameterCache.putObject(key, parameter); // 儲存過程的處理 return list; } // 實際上在PerpetualCache之前,Cache會被裝飾處理,得到最終的包裝物件
2. Namespace Mapper快取 全域性引數設定:cacheEnalbled Value=true開啟全域性二級快取;在當前Namespace中設定<cache type="xxx.cache"></cache>開啟當前Namespace的二級快取【type=xxx是自定義快取,整合其它快取的途徑】,在當前Namespace中使用useCache=false來禁用二級快取,預設flushCache=true重新整理【清空】快取。其作用域在當前Namespace,執行方式同SqlSession快取類似。
二級快取的開啟是通過CachingExecutor代理包裝Executor來實現的,對於資料的獲取首先在CachingExecutor中查詢,如果獲取不到再通過Executor delegate在SqlSession中查詢,其實現原理如下所示: public <E> List<E> query(ms, parameterObject, rowBounds, resultHandler, CacheKey key, boundSql){ Cache cache = ms.getCache(); // Cache是全域性變數,會被多個SqlSession共享 if (cache != null) { // 簡單流程:二級快取》SqlSession快取》資料庫操作 if (ms.isUseCache() && resultHandler == null) { List<E> list = (List<E>) tcm.getObject(cache, key); // 注意TransactionalCache中的裝飾順序 // SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache if (list == null) { // 快取獲取不到值,通過SqlSession查詢後儲存到二級快取 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // tcm:TransactionalCacheManager } return list; // 二級快取中獲取到值就直接返回,在分散式環境下很有可能是髒資料 } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
3. 總結 MyBatis的快取的實現都比較簡單,都是對HashMap的簡單運用,MyBatis本身提供的多種快取實現運用了裝飾者模式,對於自定義快取的實現只需要實現MyBatis提供的Cache介面即可。為保證資料的實時有效性,避免引起髒資料尤其是在分散式的環境下,通常情況下不會開啟二級快取,亦或是結合第三方快取Ehcache或者Redis來實現。
個人感覺MyBatis和JPA的快取在網際網路高併發和分散式的場景下都顯得很雞肋,就本人而言覺得應該儘量避免使用,可能技術有限理解不到位。
Mapper配置檔案中的cache節點會被解析到XMLMapperBuilder例項中的builderAssistant屬性中的currentCache值裡。Cache物件中的eviction引數中的演算法: (1)Least Recently Used【LRU】最近最少使用演算法:如果快取中的容量已滿,將快取中最近最少被使用的快取記錄清除掉,然後新增新記錄,注意同Least Frequently Used【LFU,MyBatis未提供的演算法】的區別; (2)First in first out【FIFO】先進先出演算法:如果快取中的容量已滿,將最先進入快取中的資料清除掉; (3)Scheduled指定時間間隔清空演算法:該演算法會以指定的某一個時間間隔將Cache快取中的資料清空; (4)Soft軟引用:JVM處理,移除基於垃圾回收器狀態和軟引用規則的物件; (5)Weak弱引用:JVM處理,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。