MyBatis提供查詢緩存,如果緩存中有數(shù)據(jù),則直接從緩存中獲取,沒有則從數(shù)據(jù)庫查詢,用以減輕數(shù)據(jù)壓力,提高系統(tǒng)性能。MyBatis的緩存分為一級緩存和二級緩存,默認(rèn)情況下一級緩存是開啟的,且是不能關(guān)閉的。一級緩存是SqlSession級別的,當(dāng)同一個 SqlSession中進(jìn)行相同的sql查詢時,第二次以后的查詢都是從一級緩存中獲取,一級緩存最多緩存1024條sql。二級緩存是Mapper級別的,可以跨SqlSession,不同SqlSession可共享。
MyBatis緩存機制

MyBatis緩存實現(xiàn)原理

MyBatis認(rèn)為的完全相同的查詢,只要保證statementId,rowBounds,最后生成的SQL語句,以及這個SQL語句所需要的參數(shù)完全一致就可以了。
一級緩存
一級緩存是如何組織的?
一級緩存是SqlSession級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造 sqlSession對象,并為SqlSession對象創(chuàng)建一個Executor執(zhí)行器,而緩存信息以HashMap的形式維護(hù)在這個Executor中,MyBatis將緩存和緩存操作封裝在Cache中。

一級緩存原理
第一次發(fā)出一個查詢sql,sql查詢結(jié)果寫入SqlSession的一級緩存中,緩存使用的數(shù)據(jù)結(jié)構(gòu)為(HashMap)。
同一個SqlSession發(fā)出第二次查詢時,就從緩存中獲取。如果兩次中間出現(xiàn)commit操作(update/insert/delete),則本SqlSession的一級緩存全部清空(為了緩存中記錄最新的數(shù)據(jù),避免臟讀),從數(shù)據(jù)庫查詢。
- key:mapperID+offset+limit+Sql+所有的入?yún)?/strong>
- value:查詢結(jié)果
一級緩存的生命周期
- 開啟數(shù)據(jù)庫會話,創(chuàng)建新的SqlSession對象,SqlSession對象中會有一個Executor對象,Executor對象持有新的PerpetualCache對象;會話結(jié)束時,SqlSession對象以及Executor對象和PerpetualCache對象一并銷毀。
- SqlSession調(diào)用close()方法,會釋放掉一級緩存PerpetualCache對象,一級緩存不可用。
- 如果SqlSession調(diào)用clearCache()方法,會清空PerpetualCache對象,但對象仍可用。
- SqlSession執(zhí)行任何一個insert()、update()、delete()操作,會清空PerpetualCache對象,但對象仍可用。
一級緩存的工作流程
- 對于某個查詢,根據(jù)statementId、params、rowBounds來構(gòu)建key值,根據(jù)key值去緩存Cache中查找結(jié)果;
- 判斷從Cache中根據(jù)特定的key值取的數(shù)據(jù)數(shù)據(jù)是否為空,即是否命中;
- 如果命中,則直接返回查詢結(jié)果;
- 如果沒有命中:
- 數(shù)據(jù)庫中查詢結(jié)果;
- 將key和查詢結(jié)果分別作為key和value存入Cache緩存中;
- 查詢結(jié)果返回。
- 結(jié)束。
一級緩存性能分析
- Session級別的一級緩存設(shè)計比較簡單,只使用了HashMap來維護(hù),并沒有對HashMap的容量和大小進(jìn)行限制。
- SqlSession的生存時間很短。使用一個SqlSession對象執(zhí)行的操作不會太多,執(zhí)行完就會消亡;
- 對于某一個SqlSession對象而言,只要執(zhí)行commit操作(update、insert、delete),都會將這個SqlSession對象中對應(yīng)的一級緩存清空掉,所以一般情況下不會出現(xiàn)緩存過大,影響JVM內(nèi)存空間的問題;
- 可以手動地釋放掉SqlSession對象中的緩存。
- 一級緩存是粗粒度緩存,沒有更新緩存和緩存過期概念。
- 對于數(shù)據(jù)變化頻率很大,并且需要高時效準(zhǔn)確性的數(shù)據(jù)要求,我們使用SqlSession查詢的時候,要控制好SqlSession的生存時間,SqlSession的生存時間越長,它其中緩存的數(shù)據(jù)有可能就越舊,從而造成和真實數(shù)據(jù)庫的誤差;同時對于這種情況,用戶也可以手動地適時清空SqlSession中的緩存;
- 對于只執(zhí)行、并且頻繁執(zhí)行大范圍的select操作的SqlSession對象,SqlSession對象的生存時間不應(yīng)過長。
二級緩存
二級緩存是mapper級別的,mapper以命名空間為單位創(chuàng)建緩存數(shù)據(jù)結(jié)構(gòu)(HashMap),二級緩存通過CachingExecutor(Executor的代理對象)實現(xiàn)。
二級緩存原理
所有查詢操作,在CachingExecutor都會先匹配緩存中是否存在,否則查詢數(shù)據(jù)庫。
- key:mapperID+offset+limit+Sql+所有的入?yún)?/strong>
- value:查詢結(jié)果
二級緩存工作模式
二級緩存機制關(guān)鍵在于Executor對象。若配置【cacheEnable=true】,則為SqlSession創(chuàng)建Executor加上一個裝飾者(CachingExecutor)。這時由CachingExecutor對象來完成操作請求。
CachingExecutor對于查詢請求,先判斷在Application級別的二級緩存是否有結(jié)果,如果有直接返回結(jié)果。若沒有,再交給真正的Executor對象來完成查詢,之后CachingExecutor會將真正Executor返回的結(jié)果放置在緩存中,在返回給用戶。
使用二級緩存工作模式.png
二級緩存劃分
MyBatis并不簡單將Application就只有一個Cache緩存對象,它將緩存劃分的更新,即是mapper級別的(即每一個mapper一個緩存對象)。
1. 為每一個mapper分配一個緩存對象(對于每一個mapper.xml,在其中使用<cache> 節(jié)點)。

2. 多個mapper共用一個緩存對象(使用<cache-ref namespace="">節(jié)點,來指定你的這個Mapper使用到了哪一個Mapper的Cache緩存)。

二級緩存的開啟
MyBatis對二級緩存的支持粒度較細(xì),需要具體指定到某條查詢語句上。如果要開啟二級緩存,需做到如下三點:
- MyBatis支持二級緩存的總開關(guān):全局配置變量參數(shù)【cacheEnabled=true】。
- 該select語句所在的Mapper,配置了<cache> 或<cached-ref>節(jié)點,并且有效。
- 該select語句的參數(shù) useCache=true。
二級緩存的實現(xiàn)
MyBatis 對于二級緩存的實現(xiàn)非常靈活,自己內(nèi)部實現(xiàn)了Cache的一系列的實現(xiàn)類,并提供了各種緩存刷新策略LRU、FIFO等。同時它也允許自定義Cache接口實現(xiàn),需實現(xiàn)org.apache.ibatis.cache.Cache接口,然后將Cache的實現(xiàn)類配置在<cache type="">節(jié)點的type屬性上。此外,它也支持與第三方緩存庫如Memecached的集成。
二級緩存的實現(xiàn)有三種選擇:
-
MyBatis自身提供的緩存實現(xiàn);
-
MyBatis定義了大量的Cache的裝飾器來增強Cache緩存的功能
Cache裝飾器.png -
對于每個Cache而言,都有容量限制,MyBatis提供了各種策略來對Cache緩存的容量進(jìn)行控制,以及對于Cache中的數(shù)據(jù)進(jìn)行刷新和置換。主要有以下幾個刷新和置換策略:
LRU:Least Recently Used,最近最少使用算法,即緩存中容量滿了,會將緩存最近最少使用的數(shù)據(jù)清楚,添加上新的記錄。
FIFO:First In First Out,先進(jìn)先出算法,即緩存中容量滿了,會將最先進(jìn)來的數(shù)據(jù)清楚。
Scheduled:指定時間間隔清空算法,該算法會以指定的某一個時間間隔將Cache緩存中的數(shù)據(jù)清空。
-
用戶自定義的Cache接口實現(xiàn);
跟第三方內(nèi)存緩存庫的集成;
二級緩存應(yīng)用場景
-
使用場景
對于訪問響應(yīng)速度要求高,但是實時性不高的查詢,可以采用二級緩存技術(shù)。 -
注意事項
在使用二級緩存的時候,要設(shè)置一下刷新間隔(cache標(biāo)簽中有一個flashInterval屬性)來定時刷新二級緩存,這個刷新間隔根據(jù)具體需求來設(shè)置,比如設(shè)置30分鐘、60分鐘等,單位為毫秒。
二級緩存局限性
Mybatis二級緩存對細(xì)粒度的數(shù)據(jù)級別的緩存實現(xiàn)不好。
場景
對商品信息進(jìn)行緩存,由于商品信息查詢訪問量大,但是要求用戶每次查詢都是最新的商品信息,此時如果使用二級緩存,就無法實現(xiàn)當(dāng)一個商品發(fā)生變化只刷新該商品的緩存信息而不刷新其他商品緩存信息,因為二級緩存是mapper級別的,當(dāng)一個商品的信息發(fā)送更新,所有的商品信息緩存數(shù)據(jù)都會清空。解決方法
此類問題,需要在業(yè)務(wù)層根據(jù)需要對數(shù)據(jù)有針對性的緩存。
比如可以對經(jīng)常變化的 數(shù)據(jù)操作單獨放到另一個namespace的mapper中。

