Ⅰ Mybatis的緩存講解
前段時間阿粉的一個朋友和阿粉吃飯,在吃飯的時候和阿粉瘋狂的吐槽面試官,說面試官問的問題都是些什麼問題呀,我一個幹了三四年的開發,也不說問點靠譜的,阿粉很好奇,問題問完基礎的,一般不都是根據你自己的簡歷進行提問么?而接下來他說的出來的問題,阿粉表示,阿粉需要繼續學習了。
說到這個,讀者大人們肯定心想,阿粉是在開玩笑么?你一個 Java 程序員,你不知道Mybatis是啥么?不就是個持久層的框架么,這東西有啥好說的呢?但是阿粉還是要給大家說。
為什麼說 Mybatis 是一個半自動 ORM 的框架呢?
ORM,是Object和Relation之間的映射,而Mybatis 在查詢關聯對象或關聯集合對象時,需要手動編寫 sql 來完成,所以,稱之為半自動 ORM 框架,而Hibernate 屬於全自動 ORM 映射工具,使用 Hibernate 查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接獲取,所以它是全自動的。
這也是為什麼有些面試官在面試初級程序員的時候,很喜歡說,你覺得 Mybatis , 和 Hibernate 都有什麼優缺點,為啥你們選擇使用的 Mybatis 而不選擇使用 Hibernate 呢?
我們都說了 Mybatis是什麼了,接下來肯定需要說說面試官都問了什麼問題,能讓阿粉的朋友變得非常猶豫。
當我們面試的時候,說完這個,一般情況下,面試官一定會追問下去,畢竟技術就是要問到你的知識盲區才會停止。
那我們就來畫個圖表示一下一級緩存
那面試官肯定會說,直接從資料庫查不就行了,為啥要一級緩存呢?
當我們使用MyBatis開啟一次和資料庫的會話時, MyBatis 會創建出一個 SqlSession 對象表示一次與資料庫之間的信息傳遞,在我們執行 SQL 語句的過程中,們可能會反復執行完全相同的查詢語句,如果不採取一些措施,我們每一次查詢都會查詢一次資料庫,而如果在極短的時間內做了很多次相同的查詢操作,那麼這些查詢返回的結果很可能相同。
也就是說,如果我們在短時間內,頻繁的去執行一條 SQL ,查詢返回的結果本來應該是改變了,但是我們查詢出來的時候,會出現結果一致的情況,正是為了解決這種問題,也為了減輕資料庫的開銷,所以 Mybatis 默認開啟了一級緩存。
Mybatis 的二級緩存一般如果你不對他進行設置,他是不會開啟的,而二級緩存是什麼呢?Mybatis 中的二級緩存實際上就是 mapper 級別的緩存,而這時候肯定會有人說,那麼不同之間的 Mapper 是同一個緩存么?
答案是否定的,他不是一個,Mapper 級別的緩存實際上就是相同的 Mapper 使用的是一個二級緩存,但是在二級緩存中,又有多個不同的 SqlSession ,而不同的 Mapper 之間的二級緩存也就是互相不會影響的。
就類似下面的圖
這二級緩存是不是就看起來有點意思了?
那怎麼能夠開啟二級緩存呢?
1.MyBatis 配置文件
2.MyBatis 要求返回的 POJO 必須是可序列化的
3.Mapper 的 xml 配置文件中加入 標簽
既然我們想要了解這個二級緩存,那麼必然,我們還得知道它裡面的配置都有哪些含義。
我們先從標簽看起,然後從源碼裡面看都有哪些配置信息提供給我們使用:
blocking : 直譯就是調度,而在 Mybatis 中,如果緩存中找不到對應的 key ,是否會一直 blocking ,直到有對應的數據進入緩存。
eviction : 緩存回收策略
而緩存回收策略,在源碼中是有直接體現的,那麼他們分別都對應了什麼形式呢?
大家雖然看著 PERPETUAL 排在了第一位,但是它可不是默認的,在 Mybatis 的緩存策略裡面,默認的是 LRU 。
PERPETUAL :
源代碼如下:
恩?看著是不是有點眼熟,它怎麼就只是包裝了 HashMap ? 你還別奇怪,他還真的就是使用的 HashMap ,不得不說,雖然人家是使用的 HashMap ,但是那可是比咱們寫的高端多了。
既然使用 HashMap ,那麼必然就會有Key,那麼他們的Key是怎麼設計的?
CacheKey:
確實牛逼,至於內部如何初始化,如何進行操作,大家有興趣的可以去閱讀一下源碼,導入個源碼包,打開自己看一下。
FIFO : 先進先出緩沖淘汰策略
在 FIFO 淘汰策略中使用了 Java 中的 Deque,而 Deque 一種常用的數據結構,可以將隊列看做是一種特殊的線性表,該結構遵循的先進先出原則。Java中,LinkedList實現了Queue介面,因為LinkedList進行插入、刪除操作效率較高。
當你看完這個源碼的時候,是不是就感覺源碼其實也沒有那麼難看懂,裡面都是我們已經掌握好的知識,只不過中間做了一些操作,進行了一些封裝。
LRU : 最近最少使用的緩存策略
而 LUR 演算法,阿粉之前都說過,如果對這個演算法感興趣的話,文章地址給大家送上,經典的 LRU 演算法,你真的了解嗎?
而我們需要看的源碼則是在 Mybatis 中的源碼,
SOFT : 基於垃圾回收器狀態和軟引用規則的對象
在看到基於垃圾回收器的時候,阿粉就已經開始興奮了,竟然有GC的事情,那還不趕緊看看,這如此高大上(裝杯)的事情,來瞅瞅吧!
WEAK : 基於垃圾收集器狀態和弱引用規則的對象
WeakCache在實現上與SoftCache幾乎相同,只是把引用對象由SoftReference軟引用換成了WeakReference弱引用。
在這里阿粉也就不再多說了,關於 Mybatis 的二級緩存,你了解了么?下次遇到面試官問這個的時候,你應該知道怎麼成功(裝杯)不被打了吧。
Ⅱ mybatis的緩存機制是怎麼樣的
MyBatis將數據緩存設計成兩級結構,分為一級緩存、二級緩存:
一級緩存是Session會話級別的緩存,位於表示一次資料庫會話的SqlSession對象之中,又被稱之為本地緩存。一級緩存是MyBatis內部實現的一個特性,用戶不能配置,默認情況下自動支持的緩存,用戶沒有定製它的權利(不過這也不是絕對的,可以通過開發插件對它進行修改);
二級緩存是Application應用級別的緩存,它的是生命周期很長,跟Application的聲明周期一樣,也就是說它的作用范圍是整個Application應用。
Ⅲ mybatis二級緩存原理
mybatis篇
一級緩存的作用域是Sqlsession級別的,也就是說不同的Sqlsession是不會走一級緩存的,那麼如果需要跨Sqlsession的緩存,就需要使用到二級緩存了。
二級緩存的話默認是關閉的,所以需要我們開啟,開啟的方式官網也有介紹,需要在mybatis-config.xml核心配置文件中開啟二級緩存功能,並且我們mapper.xml中也需要加入<cache/>標簽,二者缺一不可,後面我們看源碼就能知道為啥這兩個缺一不可。
先來看個例子
執行結果很意外,為什麼二級緩存的功能都開啟了,結果sql還是執行了2次,並沒有走緩存,其實,二級緩存還有一個要注意的點那就是必須要提交事務二級緩存才會保存記錄,因為已經是跨SqlSession共享緩存了,所以事務必須要提交,否則會讀取到因混滾導致的錯誤數據。
有個地方需要注意,二級緩存的Sqlsession中的Executor實際上是CachingExecutor
我們知道getMapper最終的執行都會走到MapperProxy類中的invoker方法,具體就來分析這個類。
最後來到了重點的地方
CacheKey我們可以認為他就是每個方法對應的一個唯一標識符。
這里我們就可以看出為什麼之前兩者必須要配置,cacheEnable開啟了才會用CachingExecutor包裝一下BaseExecutor,而<cache/>標簽只有配置了才會走緩存的邏輯
這里的tcm
到這,我們就差不多揭開了二級緩存的秘密,重要的還是<cache/>這個標簽,因為它的存在就對應著每個mapper.xml中的一個具體Cache類,而這個類在每個mapper.xml中又是同一個,所以最終的值是放入了Cache類中,key為CacheKey,value就是sql執行的結果。
至於為什麼需要事務提交才能命中二級緩存,我們看下put方法就知道
這里的putObject並沒有真正的把值存入Cache中,而是存入了待提交的Map中,所以再來看下commit做了什麼
具體看tcm.commit()
而這里可以看到此處會遍歷所有的TransactionCache並執行commit方法
真相就出來了,會遍歷待提交的Map然後把裡面的值都存入Cache中,所以後面的查詢就能直接從Cache中拿到值了。
總結
二級緩存先會把Sqlsession中的Executor包裝成包裝成CacheingExecutor,所有的sql都會經過這個類,而該類通過mapper.xml中配置的唯一<cache/>標簽生成的Cache類存放每個方法執行的結果
Ⅳ mybatis的緩存有幾種
1、一級緩存
MyBatis默認開啟了一級緩存,一級緩存是在SqlSession 層面進行緩存的。即,同一個SqlSession ,多次調用同一個Mapper和同一個方法的同一個參數,只會進行一次資料庫查詢,然後把數據緩存到緩沖中,以後直接先從緩存中取出數據,不會直接去查資料庫。
但是不同的SqlSession對象,因為不用的SqlSession都是相互隔離的,所以相同的Mapper、參數和方法,他還是會再次發送到SQL到資料庫去執行,返回結果。
2、二級緩存
為了克服這個問題,需要開啟二級緩存,是的緩存zaiSqlSessionFactory層面給各個SqlSession 對象共享。默認二級緩存是不開啟的,需要手動進行配置。
<cache/>
如果這樣配置的話,很多其他的配置就會被默認進行,如:
映射文件所有的select 語句會被緩存
映射文件的所有的insert、update和delete語句會刷新緩存
緩存會使用默認的Least Recently Used(LRU,最近最少使用原則)的演算法來回收緩存空間
根據時間表,比如No Flush Interval,(CNFI,沒有刷新間隔),緩存不會以任何時間順序來刷新
緩存會存儲列表集合或對象(無論查詢方法返回什麼)的1024個引用
緩存會被視為是read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而且可以很安全的被調用者修改,不幹擾其他調用者或縣城所作的潛在修改
- <cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
eviction:緩存回收策略
- LRU:最少使用原則,移除最長時間不使用的對象
- FIFO:先進先出原則,按照對象進入緩存順序進行回收
- SOFT:軟引用,移除基於垃圾回收器狀態和軟引用規則的對象
- WEAK:弱引用,更積極的移除移除基於垃圾回收器狀態和弱引用規則的對象flushInterval:刷新時間間隔,單位為毫秒,這里配置的100毫秒。如果不配置,那麼只有在進行資料庫修改操作才會被動刷新緩存區
size:引用額數目,代表緩存最多可以存儲的對象個數
readOnly:是否只讀,如果為true,則所有相同的sql語句返回的是同一個對象(有助於提高性能,但並發操作同一條數據時,可能不安全),如果設置為false,則相同的sql,後面訪問的是cache的clone副本。
useCache配置
如果一條語句每次都需要最新的數據,就意味著每次都需要從資料庫中查詢數據,可以把這個屬性設置為false,如:
- <select id="selectAll" resultMap="BaseResultMap" useCache="false">
刷新緩存(就是清空緩存)
二級緩存默認會在insert、update、delete操作後刷新緩存,可以手動配置不更新緩存,如下:
- <update id="updateById" parameterType="User" flushCache="false" />
可以在開啟二級緩存時候,手動配置一些屬性
各個屬性意義如下:
可以在Mapper的具體方法下設置對二級緩存的訪問意願:
3、自定義緩存
自定義緩存對象,該對象必須實現 org.apache.ibatis.cache.Cache 介面
每次查詢資料庫前,MyBatis都會先在緩存中查找是否有該緩存對象。只有當調用了commit() 方法,MyBatis才會往緩存中寫入數據,數據記錄的鍵為數字編號+Mapper名+方法名+SQL語句+參數格式,值為返回的對象值。
Ⅳ mybatis的緩存機制是怎麼樣的
你好,正如大多數持久層框架一樣,MyBatis 同樣提供了一級緩存和二級緩存的支持。
1.緩存,其存儲作用域為 Session,當 Session flush 或 close 之後,該Session中的所有 Cache 就將清空。
2. 二級緩存與一級緩存其機制相同,默認也是採用 PerpetualCache,HashMap存儲,不同在於其存儲作用域為 Mapper(Namespace),並且可自定義存儲源,如 Ehcache。
3. 對於緩存數據更新機制,當某一個作用域(一級緩存Session/二級緩存Namespaces)的進行了 C/U/D 操作後,默認該作用域下所有 select 中的緩存將被clear。
mybatis的一級緩存作用就是在同一個session過程中,將會對相同數據的存取不通過資料庫,而是通過緩存機制提高效率。
希望對你有幫助~
Ⅵ MyBatis二級緩存帶來的問題
MyBatis二級緩存使用的在某些場景下會出問題,來看一下為什麼這么說。
假設我有一條select語句(開啟了二級緩存):
selecta.col1, a.col2, a.col3, b.col1, b.col2, b.col3fromtableA a, tableB bwherea.id= b.id;
對於tableA與tableB的操作定義在兩個Mapper中,分別叫做MapperA與MapperB,即它們屬於兩個命名空間,如果此時啟用緩存:
MapperA中執行上述sql語句查詢這6個欄位
tableB更新了col1與col2兩個欄位
MapperA再次執行上述sql語句查詢這6個欄位(前提是沒有執行過任何insert、delete、update操作)
此時問題就來了,即使第(2)步tableB更新了col1與col2兩個欄位,第(3)步MapperA走二級緩存查詢到的這6個欄位依然是原來的這6個欄位的值,因為我們從CacheKey的3組條件來看:
<select>標簽所在的Mapper的Namespace+<select>標簽的id屬性
RowBounds的offset和limit屬性,RowBounds是MyBatis用於處理分頁的一個類,offset默認為0,limit默認為Integer.MAX_VALUE
<select>標簽中定義的sql語句
對於MapperA來說,其中的任何一個條件都沒有變化,自然會將原結果返回。
這個問題對於MyBatis的二級緩存來說是一個無解的問題,因此使用MyBatis二級緩存有一個前提: 必須保證所有的增刪改查都在同一個命名空間下才行 。
Ⅶ MyBatis緩存
可能會有很多人不理解這里,二級緩存帶來的好處遠遠比不上他所隱藏的危害。
緩存是以namespace為單位的,不同namespace下的操作互不影響。
insert,update,delete操作會清空所在namespace下的全部緩存。
通常使用MyBatis Generator生成的代碼中,都是各個表獨立的,每個表都有自己的namespace。
Ⅷ mybatis的緩存機制是怎麼樣的
兩者相同點 Hibernate與MyBatis都可以是通過SessionFactoryBuider由XML配置文件生成SessionFactory,然後由SessionFactory 生成Session,最後由Session來開啟執行事務和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。 Hibernate和MyBatis都支持JDBC和JTA事務處理。 Mybatis優勢 MyBatis可以進行更為細致的SQL優化,可以減少查詢欄位。 MyBatis容易掌握,而Hibernate門檻較高。 Hibernate優勢 Hibernate的DAO層開發比MyBatis簡單,Mybatis需要維護SQL和結果映射。 Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。 Hibernate資料庫移植性很好,MyBatis的資料庫移植性不好,不同的資料庫需要寫不同SQL。 Hibernate有更好的二級緩存機制,可以使用第三方緩存。MyBatis本身提供的緩存機制不佳。
Ⅸ mybatis的緩存機制是怎麼樣的
MyBatis數據緩存設計兩級結構級緩存、二級緩存:
級緩存Session級別緩存位於表示資料庫SqlSession象稱本緩存級緩存MyBatis內部實現特性用戶能配置默認情況自支持緩存用戶沒定製權利(絕通發插件進行修改);
二級緩存Application應用級別緩存命周期跟Application聲明周期說作用范圍整Application應用
Ⅹ mybatis一級緩存內存佔用過大的問題
內存佔用過大可以通過flushCache="true"或者where <隨機數>=<隨機數>去除MyBatis的一級緩存來解決。
1、一級緩存是SqlSession級別的緩存 —— 它是各自獨立的。
在操作資料庫時需要構造sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。
不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。
2、二級緩存是mapper級別的緩存 —— 它是多個 SqlSession 共享的。
多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。