自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MyBatis緩存你了解多少?會(huì)用嗎?

存儲(chǔ) 存儲(chǔ)軟件
我的mybatis bean是由spring 來管理的,已經(jīng)屏蔽了sqlSession這個(gè)東西了?那怎么的一次操作才算是一次sqlSession呢?

[[416832]]

本文轉(zhuǎn)載自微信公眾號(hào)「三不猴子」,作者sanbuhouzi。轉(zhuǎn)載本文請(qǐng)聯(lián)系三不猴子公眾號(hào)。

MyBatis緩存你了解多少?會(huì)用嗎?

MyBatis緩存介紹

正如大多數(shù)持久層框架一樣,MyBatis 同樣提供了一級(jí)緩存和二級(jí)緩存的支持

  1. 一級(jí)緩存: 基于PerpetualCache 的 HashMap本地緩存,其存儲(chǔ)作用域?yàn)?Session,當(dāng) Session flush 或 close 之后,該Session中的所有 Cache 就將清空。
  2. 二級(jí)緩存與一級(jí)緩存其機(jī)制相同,默認(rèn)也是采用 PerpetualCache,HashMap存儲(chǔ),不同在于其存儲(chǔ)作用域?yàn)? Mapper(Namespace),并且可自定義存儲(chǔ)源,如 Ehcache。
  3. 對(duì)于緩存數(shù)據(jù)更新機(jī)制,當(dāng)某一個(gè)作用域(一級(jí)緩存Session/二級(jí)緩存Namespaces)的進(jìn)行了 C/U/D 操作后,默認(rèn)該作用域下所有 select 中的緩存將被clear。

mybatis的相關(guān)概念

  1. SqlSession : 代表和數(shù)據(jù)庫的一次會(huì)話,向用戶提供了操作數(shù)據(jù)庫的方法。
  2. MappedStatement: 代表要發(fā)往數(shù)據(jù)庫執(zhí)行的指令,可以理解為是Sql的抽象表示。
  3. Executor: 具體用來和數(shù)據(jù)庫交互的執(zhí)行器,接受MappedStatement作為參數(shù)。
  4. 映射接口: 在接口中會(huì)要執(zhí)行的Sql用一個(gè)方法來表示,具體的Sql寫在映射文件中。
  5. 映射文件: 可以理解為是Mybatis編寫Sql的地方,通常來說每一張單表都會(huì)對(duì)應(yīng)著一個(gè)映射文件,在該文件中會(huì)定義Sql語句入?yún)⒑统鰠⒌男问健?/li>

一級(jí)緩存

  • MyBatis的一級(jí)查詢緩存(也叫作本地緩存)是基于org.apache.ibatis.cache.impl.PerpetualCache 類的 HashMap本地緩存,其作用域是SqlSession
  • 在同一個(gè)SqlSession中兩次執(zhí)行相同的 sql 查詢語句,第一次執(zhí)行完畢后,會(huì)將查詢結(jié)果寫入到緩存中,第二次會(huì)從緩存中直接獲取數(shù)據(jù),而不再到數(shù)據(jù)庫中進(jìn)行查詢,這樣就減少了數(shù)據(jù)庫的訪問,從而提高查詢效率。
  • 當(dāng)一個(gè) SqlSession 結(jié)束后,該 SqlSession 中的一級(jí)查詢緩存也就不存在了。
  • myBatis 默認(rèn)一級(jí)查詢緩存是開啟狀態(tài),且不能關(guān)閉。
  • 增刪改會(huì)清空緩存,無論是否commit 當(dāng)SqlSession關(guān)閉和提交時(shí),會(huì)清空一級(jí)緩存

可能你會(huì)有疑惑,我的mybatis bean是由spring 來管理的,已經(jīng)屏蔽了sqlSession這個(gè)東西了?那怎么的一次操作才算是一次sqlSession呢?

  • spring整合mybatis后,非事務(wù)環(huán)境下,每次操作數(shù)據(jù)庫都使用新的sqlSession對(duì)象。因此mybatis的一級(jí)緩存無法使用(一級(jí)緩存針對(duì)同一個(gè)sqlsession有效)
  • 在開啟事物的情況之下,spring使用threadLocal獲取當(dāng)前資源綁定同一個(gè)sqlSession,因此此時(shí)一級(jí)緩存是有效的 在開啟以及緩存的時(shí)候查詢得到的對(duì)象是同一個(gè)對(duì)象。這種情況下會(huì)出現(xiàn)一個(gè)問題。我們先看一下代碼。
  1. public void listMybatisModel() { 
  2.         List<MybatisModel> mybatisModels = mapper.listMybatisModel(); 
  3.         List<MybatisModel> mybatisModelsOther = mapper.listMybatisModel(); 
  4.         System.out.println(mybatisModels == mybatisModelsOther); 
  5.         System.out.println("list count: " + mybatisModels.size()); 
  6.     } 
  1. System.out.println(mybatisModels == mybatisModelsOther); 

輸出結(jié)果竟然是true,這樣說來是同一個(gè)對(duì)象。會(huì)出現(xiàn)這種場景,第一次查出來的對(duì)象然后修改了,第二次查出來的就是修改后的對(duì)象。

一級(jí)緩存實(shí)現(xiàn)

對(duì)SqlSession的操作mybatis內(nèi)部都是通過Executor來執(zhí)行的。Executor的生命周期和SqlSession是一致的。Mybatis在Executor中創(chuàng)建了一級(jí)緩存,基于PerpetualCache 類的 HashMap

二級(jí)緩存

  • MyBatis的二級(jí)緩存是mapper范圍級(jí)別的
  • SqlSession關(guān)閉后才會(huì)將數(shù)據(jù)寫到二級(jí)緩存區(qū)域
  • 增刪改操作,無論是否進(jìn)行提交commit(),均會(huì)清空一級(jí)、二級(jí)緩存
  • 二級(jí)緩存是默認(rèn)開啟的。(想開啟就不必做任何配置)
  • 二級(jí)緩存會(huì)使用 Least Recently Used (LRU,最近最少使用的)算法來收回。
  • 根據(jù)時(shí)間表(如 no Flush Interval ,沒有刷新間隔),緩存不會(huì)以任何時(shí)間順序來刷新 。
  • 緩存會(huì)存儲(chǔ)集合或?qū)ο?無論查詢方法返回什么類型的值)的 1024 個(gè)引用。
  • 緩存會(huì)被視為 read/write (可讀/可寫)的,意味著對(duì)象檢索不是共享的,而且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改 。

用下面這張圖描述一級(jí)緩存和二級(jí)緩存的關(guān)系。

配置二級(jí)緩存

在保證二級(jí)緩存的全局配置開啟的情況下,給某個(gè)xml開啟二級(jí)緩存只需要在xml中添加即可

  1. // mybatis-config.xml 中配置 
  2. <settings> 
  3.     默認(rèn)值為 true。即二級(jí)緩存默認(rèn)是開啟的 
  4.     <setting name="cacheEnabled" value="true"/> 
  5. </settings> 
  6.  
  7. // 具體mapper.xml 中配置 
  8. <mapper namespace="cn.itcast.mybatis.mapper.UserMapper"
  9.  <!-- 開啟本mapper的namespace下的二級(jí)緩存 
  10.  type:指定cache接口的實(shí)現(xiàn)類的類型,mybatis默認(rèn)使用PerpetualCache 
  11.  要和ehcache整合,需要配置type為ehcache實(shí)現(xiàn)cache接口的類型--> 
  12.  
  13.  <cache /> 
  14.  
  15. </mapper> 

 

如果想要設(shè)置增刪改操作的時(shí)候不清空二級(jí)緩存的話,可以在其insert或delete或update中添加屬性flushCache=”false”,默認(rèn)為 true。

  1. <delete id="deleteStudent" flushCache="false"
  2.     DELETE FROM user where id=#{id} 
  3. </delete

 

通過以下一些配置可以修改一些緩存參數(shù)。

  1. <cache 
  2.     eviction= "FIFO" 
  3.     flushlnterval="600000" 
  4.     size="512" 
  5.     readOnly="true" /> 

配置創(chuàng)建了一個(gè)FIFO緩存,并每隔 60 秒刷新一次,存儲(chǔ)集合或?qū)ο蟮?12個(gè)引用, 而且返回的對(duì)象被認(rèn)為是只讀的,因此在不同線程中的調(diào)用者之間修改它們會(huì)導(dǎo)致沖突。cache可以配置的屬性如下。eviction (收回策略)

LRU (最近最少使用的: 移除最長時(shí)間不被使用的對(duì)象,這是默認(rèn)值 。FIFO (先進(jìn)先出〉 :按對(duì)象進(jìn)入緩存的順序來移除它們 。SOFT (軟引用) :移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對(duì)象 。WEAK (弱引用) :更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對(duì)象

  • flushinterval(刷新間隔)。可以被設(shè)置為任意的正整數(shù),而且它們代表一個(gè)合理 的毫秒形式的時(shí)間段。默認(rèn)情況不設(shè)置,即沒有刷新間隔,緩存僅僅在調(diào)用語句時(shí)刷新。
  • size (引用數(shù)目)。可以被設(shè)置為任意正整數(shù),要記住緩存的對(duì)象數(shù)目和運(yùn)行環(huán)境的可用內(nèi)存資源數(shù)目。默認(rèn)值是 1024 。
  • readOnly (只讀)。屬性可以被設(shè)置為 true 或 false 。只讀的緩存會(huì)給所有調(diào)用者 返回緩存對(duì)象的相同實(shí)例,因此這些對(duì)象不能被修改,這提供了很重要的性能優(yōu)勢。可讀寫的緩存會(huì)通過序列化返回緩存對(duì)象的拷貝,這種方式會(huì)慢一些,但是安全,因此默認(rèn)是false。

當(dāng)只使用注解方式配置二級(jí)緩存時(shí),如果在Mapper接口中,則需要增加如下配置 。

  1. @CacheNamespace ( 
  2. eviction = FifoCache.class , 
  3. flushinterval = 60000 , 
  4. size = 512 , 
  5. readWrite = true
  6. public interface Mapper { 
  7.      

括號(hào)內(nèi)的內(nèi)容是配置緩存屬性。

Mapper 接口和對(duì)應(yīng)的 XML 文件是相同的命名空間,想使用二級(jí)緩存,兩者必須同時(shí)配置(如果接口不存在使用注解方式的方法,可以只在 XML 中配置〉,因此按照上面的方 式進(jìn)行配置就會(huì)出錯(cuò) , 這個(gè)時(shí)候應(yīng)該使用參照緩存。在 Mapper 接口中,參照緩存配置如下 。

  1. @CacheNarnespaceRef(RoleMapper.class) 
  2. public interface RoleMapper { 

因?yàn)橄胱?RoleMapper 接口中的注解方法和 XML中的方法使用相同的緩存,因此使用參照緩存配置RoleMapper.class,這樣就會(huì)使用命名空間為xx.xxx.xxx.xxx.RoleMapper的緩存配置,即RoleMapper.xml 中配置的緩存 。Mapper 接口可以通過注解引用XML 映射文件或者其他接口的緩存,在 XML 中也可以配置參照緩存,如可以在 RoleMapper.xml 中進(jìn)行如下修改 。

  1. <cache-ref narnespace="xxx.xxx.xxx.xxx.RoleMapper"/> 

這樣配置后XML 就會(huì)引用 Mapper 接口中配置的二級(jí)緩存,同樣可以避免同時(shí)配置二級(jí)緩存導(dǎo)致的沖突。MyBatis 中很少會(huì)同時(shí)使用 Mapper 接口注解方式和XML映射文件,所以參照緩存并不是為了解決這個(gè)問題而設(shè)計(jì)的。參照緩存除了能夠通過引用其他緩存減少配置外,主要的作用是解決臟讀。

MyBatis使用SerializedCache(org.apache.ibaits.cache.decorators.SerializedCache)序列化緩存來實(shí)現(xiàn)可讀寫緩存類,井通過序列化和反序列化來保證通過緩存獲取數(shù)據(jù)時(shí),得到的是一個(gè)新的實(shí)例。因此,如果配置為只讀緩存,MyBatis就會(huì)使用Map來存儲(chǔ)緩存值,這種情況下,從緩存中獲取的對(duì)象就是同一個(gè)實(shí)例。因?yàn)槭褂每勺x寫緩存,可以使用SerializedCache序列化緩存。這個(gè)緩存類要求所有被序列化的對(duì)象必須實(shí)現(xiàn) Serializable (java.io.Serializable)接口 雖然使用序列化得到的對(duì)象都是不一樣的對(duì)象修改時(shí)都是互不影響,但是還是不安全的。

臟讀的產(chǎn)生

Mybatis的二級(jí)緩存是和命名空間綁定的,所以通常情況下每一個(gè)Mapper映射文件都有自己的二級(jí)緩存,不同的mapper的二級(jí)緩存互不影響。

  • 引起臟讀的操作通常發(fā)生在多表關(guān)聯(lián)操作中,比如在兩個(gè)不同的mapper中都涉及到同一個(gè)表的增刪改查操作,當(dāng)其中一個(gè)mapper對(duì)這張表進(jìn)行查詢操作,此時(shí)另一個(gè)mapper進(jìn)行了更新操作刷新緩存,然后第一個(gè)mapper又查詢了一次,那么這次查詢出的數(shù)據(jù)是臟數(shù)據(jù)。出現(xiàn)臟讀的原因是他們的操作的緩存并不是同一個(gè)。

臟讀的避免

  • mapper中的操作以單表操作為主,避免在關(guān)聯(lián)操作中使用mapper
  • 使用參照緩存

集成EhCache緩存

  • 緩存數(shù)據(jù)有內(nèi)存和磁盤兩級(jí),無須擔(dān)心容量問題。
  • 緩存數(shù)據(jù)會(huì)在虛擬機(jī)重啟的過程中寫入磁盤。可以通過RMI、可插入API等方式進(jìn)行分布式緩存。
  • 具有緩存和緩存管理器的偵昕接口。

支持多緩存管理器實(shí)例以及一個(gè)實(shí)例的多個(gè)緩存區(qū)域。

1. 添加項(xiàng)目依賴

  1. <dependency> 
  2.     <groupId>org.mybatis.caches</groupId> 
  3.     <artifactId>mybatis-ehcache</artifactId> 
  4.     <version>1.0.3</version> 
  5. </dependency> 

 

2. 配置 EhCache

在 src/main/resources 目錄下新增 ehcache.xml 文件。

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.     xsi:noNamespaceSchemaLocation="ehcache.xsd" 
  4.     updateCheck="false" monitoring="autodetect" 
  5.     dynamicConfig="true"
  6.      
  7.     <diskStore path="D:/cache" /> 
  8.              
  9.  <defaultCache       
  10.   maxElementsInMemory="3000"       
  11.   eternal="false"       
  12.   copyOnRead="true" 
  13.   copyOnWrite="true" 
  14.   timeToIdleSeconds="3600"       
  15.   timeToLiveSeconds="3600"       
  16.   overflowToDisk="true"       
  17.   diskPersistent="true"/>  
  18. </ehcache> 

 

有關(guān)EhCache的詳細(xì)配置可以參考地址 http://www.ehcache.org/ehcache.xml 中的內(nèi)容。

  • copyOnRead 的含義是,判斷從緩存中讀取數(shù)據(jù)時(shí)是返回對(duì)象的引用還是復(fù)制一個(gè)對(duì)象返回。默認(rèn)情況下是false,即返回?cái)?shù)據(jù)的引用,這種情況下返回的都是相同的對(duì)象,和MyBatis默認(rèn)緩存中的只讀對(duì)象是相同的。如果設(shè)置為 true ,那就是可讀寫緩存,每次讀取緩存時(shí)都會(huì)復(fù)制一個(gè)新的實(shí)例 。
  • copyOnWrite 的含義是 ,判斷寫入緩存時(shí)是直接緩存對(duì)象的引用還是復(fù)制一個(gè)對(duì)象然后緩存,默認(rèn)也是false。如果想使用可讀寫緩存,就需要將這兩個(gè)屬性配置為true,如果使用只讀緩存,可以不配置這兩個(gè)屬性,使用默認(rèn)值 false 即可 。
  • 修改Mapper.xml中的緩存配置 ehcache-cache 提供了如下 2 個(gè)可選的緩存實(shí)現(xiàn)。
  • org.mybatis.caches.ehcache.EhcacheCache
  • org.mybatis.caches.ehcache.LoggingEhcache 這個(gè)是帶日志的緩存。

在xml中添加

  1. <cache type ="org.mybatis.caches.ehcache.EhcacheCache" /> 

只通過設(shè)置 type 屬性就可 以使用 EhCache 緩存了,這時(shí)cache的其他屬性都不會(huì)起到任何作用,針對(duì)緩存的配置都在ehcache.xml中進(jìn)行。在ehcache.xml配置文件中,只有一個(gè)默認(rèn)的緩存配置,所以配置使用EhCache緩存的Mapper映射文件都會(huì)有一個(gè)以映射文件命名空間命名的緩存。如果想針對(duì)某一個(gè)命名空間進(jìn)行配置,需要在 ehcache.xml 中添加一個(gè)和映射文件命名空間一致的緩存配置,例如針對(duì)RoleMapper可以進(jìn)行如下配置。

  1. <cache       
  2.  name="tk.mybatis.simple.mapper.RoleMapper" 
  3.  maxElementsInMemory="3000"       
  4.  eternal="false"       
  5.  copyOnRead="true" 
  6.  copyOnWrite="true" 
  7.  timeToIdleSeconds="3600"       
  8.  timeToLiveSeconds="3600"       
  9.  overflowToDisk="true"       
  10.  diskPersistent="true"/> 

集成Redis緩存

添加依賴,目前只有bata版本。

  1.       <dependency> 
  2.  <groupId>org.mybatis.caches</groupId> 
  3.  <artifactId>mybatis-redis</artifactId> 
  4.  <version>1.0.0-beta2</version> 
  5. </dependency> 

 

配置Redis 使用 Redis 前,必須有一個(gè) Redis 服務(wù),有關(guān)Redis安裝啟動(dòng)的相關(guān)內(nèi)容,可參考如下地址中的官方文檔:https://redis.io/topics/quickstart。Redis服務(wù)啟動(dòng)后,在src/main/resources 目錄下新增 redis.properties 文件 。

  1. host=localhost 
  2. port=6379 
  3. connectionTimeout=SOOO 
  4. soTimeout=SOOO 
  5. password
  6. database=O 
  7. clientName= 

修改mapper.xml中的配置。

  1. <mapper namespace = ” tk.mybat 工 s.s 工 mple.mapper.RoleMapper ” 〉 
  2. <cache type= "org.mybatis.caches.redis.RedisCache" /> 
  3. 〈 !一其他自己直一 〉 
  4. </mapper> 

 

 

配置依然很簡單, RedisCache 在保存緩存數(shù)據(jù)和獲取緩存數(shù)據(jù)時(shí),使用了Java的序列化和反序列化,因此還需要保證被緩存的對(duì)象必須實(shí)現(xiàn)Serializable接口。改為RedisCache緩存配置后, testL2Cache 測試第一次執(zhí)行時(shí)會(huì)全部成功,但是如果再次執(zhí)行,就會(huì)出錯(cuò)。這是因?yàn)镽edis作為緩存服務(wù)器,它緩存的數(shù)據(jù)和程序(或測試)的啟動(dòng)無關(guān),Redis 的緩存并不會(huì)因?yàn)閼?yīng)用的關(guān)閉而失效。所以再次執(zhí)行時(shí)沒有進(jìn)行一次數(shù)據(jù)庫查詢,所有查詢都使用緩存,測試的第一部分代碼中的rolel和role2都是直接從二級(jí)緩存中獲取數(shù)據(jù),因?yàn)槭强勺x寫緩存,所以不是相同的對(duì)象。當(dāng)需要分布式部署應(yīng)用時(shí),如果使用MyBatis自帶緩存或基礎(chǔ)的EhCahca緩存,分布式應(yīng)用會(huì)各自擁有自己的緩存,它們之間不會(huì)共享緩存 ,這種方式會(huì)消耗更多的服務(wù)器資源。如果使用類似 Redis 的緩存服務(wù),就可以將分布式應(yīng)用連接到同一個(gè)緩存服務(wù)器,實(shí)現(xiàn)分布式應(yīng)用間的緩存共享 。

 

責(zé)任編輯:武曉燕 來源: 三不猴子
相關(guān)推薦

2019-11-12 14:40:43

CPU緩存內(nèi)存

2020-03-11 20:42:34

瀏覽器緩存機(jī)制

2020-03-25 08:47:22

智能邊緣邊緣計(jì)算網(wǎng)絡(luò)

2012-12-27 10:58:24

KVMKVM概念

2021-06-06 18:22:04

PprofGopher邏輯

2023-10-25 08:17:06

Lite模式代理類

2023-10-29 08:35:47

AndroidAOP編程

2025-01-20 00:00:00

反射Java語言

2021-09-06 10:42:18

Linux命令服務(wù)器

2022-06-07 07:37:40

線程進(jìn)程開發(fā)

2019-08-07 17:18:18

云計(jì)算云原生函數(shù)

2022-02-08 12:06:12

云計(jì)算

2023-09-07 10:26:50

接口測試自動(dòng)化測試

2025-01-16 10:41:40

2011-08-23 11:03:35

ATM

2023-08-17 10:12:04

前端整潔架構(gòu)

2021-12-09 07:47:58

Flink 提交模式

2015-11-09 10:44:37

DevOpsIT運(yùn)維

2020-12-10 09:00:00

開發(fā).NET工具

2023-12-24 12:56:36

協(xié)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)