日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis源码之缓存模块分析

發(fā)布時(shí)間:2025/3/11 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis源码之缓存模块分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

緩存這個(gè)東西在很多應(yīng)用中都能看到它們的身影,這次就講講在Mybatis中的緩存是怎么應(yīng)用的,雖然說(shuō)吧Mybatis中的緩存基本不怎么用,用的更多是第三方組件redis、MongoDB、MemCache等等。

Mybatis的緩存是基于Map實(shí)現(xiàn)的,從緩存中讀寫(xiě)數(shù)據(jù)是緩存模塊的核心功能,但是除了這個(gè)功能Mybatis也提供了很多附加功能,比如防止緩存擊穿、添加緩存清空策略等等,并且這些附加的功能屬性可以隨意組合到核心功能上。

緩存在Mybatis中的使用介紹

Myabtis中有兩個(gè)緩存,一級(jí)緩存以及二級(jí)緩存,

一級(jí)緩存是默認(rèn)開(kāi)啟的,而二級(jí)緩存是在配置文件中開(kāi)啟的,默認(rèn)是開(kāi)啟,但是可以配置不開(kāi)啟,Mybatis配置參數(shù)定義

一級(jí)緩存的僅僅存在于同一SqlSession當(dāng)中,如果關(guān)閉了SqlSession的話,那么這個(gè)緩存就沒(méi)有了,

二級(jí)緩存是跨SqlSession的存在,同一個(gè)SqlSessionFactory是有效的,在多個(gè)SqlSession當(dāng)中,也會(huì)存在多個(gè)二級(jí)緩存的存在,所以就容易出現(xiàn)數(shù)據(jù)臟讀,個(gè)人建議二級(jí)緩存還是禁止比較好,使用第三方的緩存組件。

在對(duì)應(yīng)的Mapper.xml當(dāng)中使用:

eviction:使用什么類型的緩存,默認(rèn)LRU緩存,緩存滿后,將使用的最少的緩存去除;

flushInterval:刷新時(shí)間;

readOnly:是否只讀;

size:緩存大小;

blocking:是否采用阻塞策略;

<cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024"></cache>

?緩存的引用,直接引用對(duì)應(yīng)的工作空間,引用后,就會(huì)直接使用這個(gè)引用的緩存:

<cache-ref namespace="xxx.xxx.xxxDao"></cache-ref>

設(shè)計(jì)模式

先來(lái)看看這個(gè)模塊用到了哪些設(shè)計(jì)模式

裝飾器模式(Decorator Pattern)

定義:允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個(gè)包裝。

這種模式創(chuàng)建了一個(gè)裝飾類,用來(lái)包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能。

像這種組合附加功能的業(yè)務(wù)上也可以通過(guò)繼承去組合相對(duì)應(yīng)的屬性,但是繼承的方式是靜態(tài)的,并不能控制增加新的屬性,如果功能屬性較多的話,那么使用繼承就會(huì)有很多子類,可讀性不強(qiáng)。

圖示:

組件(Component):組件接口定義了全部組件類 和裝飾器實(shí)現(xiàn)的行為;

組件實(shí)現(xiàn)類(ConcreteComponent):實(shí)現(xiàn) Component接口,組件實(shí)現(xiàn)類就是被裝飾器裝飾的 原始對(duì)象,新功能或者附加功能都是通過(guò)裝飾器添加 到該類的對(duì)象上的;

裝飾器抽象類(Decorator):實(shí)現(xiàn)Component接 口的抽象類,在其中封裝了一個(gè)Component 對(duì)象, 也就是被裝飾的對(duì)象;

具體裝飾器類(ConcreteDecorator):該實(shí)現(xiàn)類 要向被裝飾的對(duì)象添加某些功能;

相對(duì)于繼承來(lái)說(shuō),使用裝飾器模式的靈活性更高,擴(kuò)展性更強(qiáng)。

裝飾器使用:

JDK中的IO輸入流與輸出流設(shè)計(jì):

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("c://a.txt")));

在Servlet?API當(dāng)中的對(duì)request的封裝類HttpServletRequestWrapper;

源碼分析

Mybatis的緩存模塊就是使用的裝飾器模式

BlockingCache示例:

同一個(gè)緩存的接口Cache(這個(gè)接口是緩存模塊的核心接口,定義了緩存的基本操作):

public interface Cache {/*** @return The identifier of this cache*/String getId();/*** @param key* Can be any object but usually it is a {@link CacheKey}* @param value* The result of a select.*/void putObject(Object key, Object value);/*** @param key* The key* @return The object stored in the cache.*/Object getObject(Object key);/*** @param key* The key* @return Not used*/Object removeObject(Object key);/*** Clears this cache instance.*/void clear();/*** Optional. This method is not called by the core.** @return The number of elements stored in the cache (not its capacity).*/int getSize();/*** Optional. As of 3.2.6 this method is no longer called by the core.* <p>* Any locking needed by the cache must be provided internally by the cache provider.** @return A ReadWriteLock*/default ReadWriteLock getReadWriteLock() {return null;}}

在不同屬性的實(shí)現(xiàn)類分別封裝一個(gè)Cache對(duì)象,實(shí)現(xiàn)了每個(gè)附加功能的組合。

FifoCache:一個(gè)先進(jìn)先出的緩存,如果緩存的數(shù)量達(dá)到了臨界點(diǎn),則將緩存時(shí)間最長(zhǎng)的那個(gè)緩存去除。

LoggingCache:附加了日志以及統(tǒng)計(jì)功能。

?LruCache:如果緩存數(shù)據(jù)達(dá)到了臨界點(diǎn),會(huì)將訪問(wèn)次數(shù)最少的緩存數(shù)據(jù)清空。

ScheduledCache:會(huì)定時(shí)清除緩存的一個(gè)緩存。

SerializedCache:一個(gè)可以序列化以及反序列化的緩存。

SoftCache:軟引用緩存,如果緩存被GC回收后,對(duì)應(yīng)的緩存數(shù)據(jù)也會(huì)被清空。

WeakCache:弱引用緩存,如果緩存被GC回收后,對(duì)應(yīng)的緩存數(shù)據(jù)也會(huì)被清空。

SynchronizedCache:同步運(yùn)行的緩存。

TransactionalCache:一個(gè)存在事務(wù)提交和回滾的緩存。

BlockingCache:包含阻塞機(jī)制的緩存

在緩存模塊中用到的緩存類實(shí)際上是PerpetualCache,以上的那些緩存類僅僅是附加功能,具體實(shí)現(xiàn)緩存功能的是PerpetualCache。

public class PerpetualCache implements Cache {private final String id;//數(shù)據(jù)被緩存的地方private final Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}@Overridepublic boolean equals(Object o) {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}if (this == o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache = (Cache) o;return getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}return getId().hashCode();}}

一個(gè)比較簡(jiǎn)單的基于Map的緩存實(shí)現(xiàn)類。

緩存擊穿

對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō),緩存是一個(gè)很好的東西,它能夠擋住大部分的數(shù)據(jù)請(qǐng)求,但是如果在緩存中找不到對(duì)應(yīng)的key和value時(shí),那么這個(gè)這些請(qǐng)求就會(huì)去請(qǐng)求數(shù)據(jù)庫(kù),如果這個(gè)請(qǐng)求是在高并發(fā)的情況或者數(shù)據(jù)是“熱數(shù)據(jù)”的情況,那么數(shù)據(jù)庫(kù)訪問(wèn)那么也會(huì)變成高并發(fā)訪問(wèn)查詢,這就是緩存擊穿了。

所以在設(shè)計(jì)緩存模塊的時(shí)候就應(yīng)該考慮到這個(gè)問(wèn)題,在Mybatis里面這個(gè)問(wèn)題已經(jīng)得到解決,BlockingCache就是解決方法。

我們先來(lái)看看緩存擊穿的一些解決辦法:

第一種:如果當(dāng)前緩存被擊穿后,不應(yīng)該讓所有的請(qǐng)求去請(qǐng)求數(shù)據(jù)庫(kù),而是只讓一個(gè)請(qǐng)求去請(qǐng)求數(shù)據(jù)庫(kù),其他線程等待,當(dāng)數(shù)據(jù)查詢完畢并緩存進(jìn)去后,再讓其他線程去查詢緩存。

但是這個(gè)方法存在一個(gè)問(wèn)題,面對(duì)大量的請(qǐng)求來(lái)說(shuō),就會(huì)有效率問(wèn)題。

第二種:既然以上方法存在效率問(wèn)題,那么就可以給同等類型的key上鎖,讓相同的請(qǐng)求只派出一個(gè)請(qǐng)求去請(qǐng)求數(shù)據(jù)庫(kù),其他同等類型key的請(qǐng)求就等待,當(dāng)請(qǐng)求到后,那么就釋放鎖其他線程回去緩存中獲取數(shù)據(jù)了。

但是如果key太多的話,會(huì)比較浪費(fèi)資源。

綜上所述:對(duì)應(yīng)這種緩存的話,個(gè)人覺(jué)得應(yīng)該要與業(yè)務(wù)掛鉤,取得一個(gè)平衡點(diǎn)取設(shè)計(jì)緩存模塊。

現(xiàn)在我們來(lái)看看BlockingCache是怎么玩的,其實(shí)就是給每個(gè)key上鎖然后,請(qǐng)求完在釋放鎖。

我目前的Mybatis版本是最新的3.5.7,可能其他版本并非使用的事CountDownLatch,而是使用的ReentrantLock,基本都是一樣,基于AQS同步,想要了解線程相關(guān)的東西,可以去看看我之前發(fā)布博客喲。

CacheKey

現(xiàn)在來(lái)看一下緩存的key是怎么組成的,緩存模塊中的key值并非直接的使用SQL語(yǔ)句中的唯一ID啥的。

在Mybatis設(shè)計(jì)緩存時(shí),就將key包裝成一個(gè)對(duì)象,只有這個(gè)對(duì)象的hashCode相等,這兩個(gè)key才能一致。

構(gòu)成CacheKey的因素有四個(gè):

1、mappedStatment的id ;

2、指定查詢結(jié)果集的范圍(分頁(yè)信息);

3、查詢所使用的SQL語(yǔ)句;

4、用戶傳遞給SQL語(yǔ)句的實(shí)際參數(shù)值;

update方法

equals方法

Mybatis的緩存基本講的差不多了。

總結(jié)

以上是生活随笔為你收集整理的Mybatis源码之缓存模块分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。