Spring中的@Cacheable開銷

Spring 3.1引入了很棒的緩存抽象層 。 最后,我們可以放棄所有本地化的方面,裝飾器和污染我們與緩存相關的業務邏輯的代碼。

從那時起,我們可以簡單地注釋重量級方法,并讓Spring和AOP機械完成工作:

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

"books"是一個緩存名稱, isbn參數成為緩存鍵,返回的Book對象將放置在該鍵下。 緩存名稱的含義取決于基礎緩存管理器(EhCache,并發映射等)– Spring使插入不同的緩存提供程序變得容易。 但是這篇文章與Spring的緩存功能無關 ...

前段時間,我的隊友正在優化底層代碼,并發現了緩存的機會。 他Swift應用@Cacheable只是為了發現代碼的性能比以前差。 他擺脫了注釋,并使用了良好的舊java.util.ConcurrentHashMap手動實現了自己的緩存。 性能要好得多。 他指責@Cacheable和Spring AOP的開銷和復雜性。 我不敢相信緩存層的性能如此之差,直到我不得不自己幾次調試Spring緩存方面(代碼中的一些討厭的錯誤,緩存無效化是CS中最難的兩件事之一 )。 好吧,緩存抽象代碼比人們期望的要復雜得多(畢竟只是獲取放入 !),但這并不一定意味著它一定那么慢嗎?

科學中,我們不相信和信任,我們進行衡量和基準測試。 因此,我寫了一個基準來精確測量@Cacheable層的開銷。 Spring中的緩存抽象層是在Spring AOP之上實現的,可以進一步在Java代理,CGLIB生成的子類或AspectJ工具的之上實現。 因此,我將測試以下配置:

  • 完全沒有緩存–無需中間層即可測量代碼的速度
  • 在業務代碼中使用ConcurrentHashMap進行手動緩存處理
  • @Cacheable與實現AOP的CGLIB
  • @Cacheable與實現AOP的java.lang.reflect.Proxy
  • @Cacheable與AspectJ的編譯時編織(如類似的基準測試所示, CTW比LTW稍快 )
  • 本地的AspectJ緩存方面–在業務代碼中的手動緩存和Spring抽象之間的某種程度

讓我重申一下:我們沒有衡量緩存的性能提升,也沒有比較各種緩存提供程序。 這就是我們的測試方法盡可能快的原因,我將使用Spring中最簡單的ConcurrentMapCacheManager 。 所以這是一個有問題的方法:

public interface Calculator {int identity(int x);}public class PlainCalculator implements Calculator {@Cacheable("identity")@Overridepublic int identity(int x) {return x;}}

我知道,我知道緩存這種方法毫無意義。 但是我想衡量緩存層的開銷(在緩存命中期間)。 每個緩存配置將具有其自己的ApplicationContext因為您不能在一個上下文中混合使用不同的代理模式:

public abstract class BaseConfig {@Beanpublic Calculator calculator() {return new PlainCalculator();}}@Configuration
class NoCachingConfig extends BaseConfig {}@Configuration
class ManualCachingConfig extends BaseConfig {@Bean@Overridepublic Calculator calculator() {return new CachingCalculatorDecorator(super.calculator());}
}@Configuration
abstract class CacheManagerConfig extends BaseConfig {@Beanpublic CacheManager cacheManager() {return new ConcurrentMapCacheManager();}}@Configuration
@EnableCaching(proxyTargetClass = true)
class CacheableCglibConfig extends CacheManagerConfig {}@Configuration
@EnableCaching(proxyTargetClass = false)
class CacheableJdkProxyConfig extends CacheManagerConfig {}@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
class CacheableAspectJWeaving extends CacheManagerConfig {@Bean@Overridepublic Calculator calculator() {return new SpringInstrumentedCalculator();}}@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
class AspectJCustomAspect extends CacheManagerConfig {@Bean@Overridepublic Calculator calculator() {return new ManuallyInstrumentedCalculator();}}

每個@Configuration類代表一個應用程序上下文。 CachingCalculatorDecorator是圍繞真正的計算器進行裝飾的裝飾器(歡迎使用1990年代):

public class CachingCalculatorDecorator implements Calculator {private final Map<Integer, Integer> cache = new java.util.concurrent.ConcurrentHashMap<Integer, Integer>();private final Calculator target;public CachingCalculatorDecorator(Calculator target) {this.target = target;}@Overridepublic int identity(int x) {final Integer existing = cache.get(x);if (existing != null) {return existing;}final int newValue = target.identity(x);cache.put(x, newValue);return newValue;}
}

SpringInstrumentedCalculatorManuallyInstrumentedCalculatorPlainCalculator完全相同,但是它們分別由AspectJ編譯時織布器(帶有Spring和自定義方面)進行檢測。 我的自定義緩存方面如下所示:

public aspect ManualCachingAspect {private final Map<Integer, Integer> cache = new ConcurrentHashMap<Integer, Integer>();pointcut cacheMethodExecution(int x): execution(int com.blogspot.nurkiewicz.cacheable.calculator.ManuallyInstrumentedCalculator.identity(int)) && args(x);Object around(int x): cacheMethodExecution(x) {final Integer existing = cache.get(x);if (existing != null) {return existing;}final Object newValue = proceed(x);cache.put(x, (Integer)newValue);return newValue;}}

經過所有準備工作,我們終于可以編寫基準測試了。 首先,我啟動所有應用程序上下文并獲取Calculator實例。 每個實例都不同。 例如, noCaching是沒有包裝的PlainCalculator實例, cacheableCglib是CGLIB生成的子類,而aspectJCustomManuallyInstrumentedCalculator的實例,其中編織了我的自定義方面。

private final Calculator noCaching = fromSpringContext(NoCachingConfig.class);
private final Calculator manualCaching = fromSpringContext(ManualCachingConfig.class);
private final Calculator cacheableCglib = fromSpringContext(CacheableCglibConfig.class);
private final Calculator cacheableJdkProxy = fromSpringContext(CacheableJdkProxyConfig.class);
private final Calculator cacheableAspectJ = fromSpringContext(CacheableAspectJWeaving.class);
private final Calculator aspectJCustom = fromSpringContext(AspectJCustomAspect.class);private static <T extends BaseConfig> Calculator fromSpringContext(Class<T> config) {return new AnnotationConfigApplicationContext(config).getBean(Calculator.class);
}

我將通過以下測試來練習每個Calculator實例。 附加的累加器是必需的,否則JVM可能會優化整個循環(!):

private int benchmarkWith(Calculator calculator, int reps) {int accum = 0;for (int i = 0; i < reps; ++i) {accum += calculator.identity(i % 16);}return accum;
}

這是完整的卡尺測試,沒有討論任何部件:

public class CacheableBenchmark extends SimpleBenchmark {//...public int timeNoCaching(int reps) {return benchmarkWith(noCaching, reps);}public int timeManualCaching(int reps) {return benchmarkWith(manualCaching, reps);}public int timeCacheableWithCglib(int reps) {return benchmarkWith(cacheableCglib, reps);}public int timeCacheableWithJdkProxy(int reps) {return benchmarkWith(cacheableJdkProxy, reps);}public int timeCacheableWithAspectJWeaving(int reps) {return benchmarkWith(cacheableAspectJ, reps);}public int timeAspectJCustom(int reps) {return benchmarkWith(aspectJCustom, reps);}
}

希望您仍在繼續我們的實驗。 現在,我們將執行Calculate.identity()數百萬次,并查看哪種緩存配置效果最佳。 由于我們僅使用16個不同的參數調用identity() ,因此幾乎永遠不會碰到方法本身,因為我們總是會遇到緩存命中的情況。 想知道結果嗎?

benchmark      ns linear runtimeNoCaching    1.77 =ManualCaching   23.84 =CacheableWithCglib 1576.42 ==============================CacheableWithJdkProxy 1551.03 =============================
CacheableWithAspectJWeaving 1514.83 ============================AspectJCustom   22.98 =


卡尺

解釋

讓我們一步一步走。 首先,在Java中調用方法相當快! 1.77 納秒 ,我們在這里談論的是我的Intel(R)Core(TM)2 Duo CPU T7300 @ 2.00GHz上的3個CPU周期 ! 如果這不能使您確信Java是快速的,那么我不知道會怎樣。 但是回到我們的測試。

手工緩存裝飾器也相當快。 當然,與純函數調用相比,它慢了一個數量級,但與所有@Scheduled基準測試相比,它仍然非常快。 我們看到下降了3個數量級 ,從1.8 ns下降到1.5μs。 我對由AspectJ支持的@Cacheable感到特別失望。 將所有緩存方面直接預編譯到我的Java .class文件中之后,我希望它比動態代理和CGLIB快得多。 但這似乎并非如此。 所有這三種Spring AOP技術都是相似的。

最大的驚喜是我自定義的AspectJ方面。 它甚至比CachingCalculatorDecorator還要快! 也許是由于裝飾器中的多態調用? 我強烈建議您在GitHub上克隆此基準測試并運行它( mvn clean test ,大約需要2分鐘)以比較您的結果。

結論

您可能想知道為什么Spring抽象層這么慢? 好吧,首先,請檢查CacheAspectSupport的核心實現-它實際上非常復雜。 其次,真的那么慢嗎? 算一下-您通常在數據庫,網絡和外部API成為瓶頸的業務應用程序中使用Spring。 您通常會看到什么延遲? 毫秒? 幾百或幾百毫秒? 現在添加2μs的開銷(最壞的情況)。 對于緩存數據庫查詢或REST調用,這是完全可以忽略的。 選擇哪種技術都沒關系

但是,如果要在非常接近金屬的地方緩存非常低級的方法,例如CPU密集型的內存中計算,那么Spring抽象層可能會顯得過大。 底線:測量!

PS: Markdown格式的本文 基準和內容均可免費獲得。

參考:來自Java和社區博客的JCG合作伙伴 Tomasz Nurkiewicz提供的@ @ Spring的可緩存開銷 。

翻譯自: https://www.javacodegeeks.com/2013/01/cacheable-overhead-in-spring.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370282.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370282.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370282.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電工接線模擬仿真軟件_VERICUT數控加工仿真軟件,最強的數控加工模擬軟件,你知道么?...

VERICUT數控加工仿真軟件,最強的數控加工模擬軟件VERICUT軟件及功能簡介1、VERICUT軟件簡介VERICUT是美國CGTech公司開發一款專業的數控加工仿真軟件&#xff0c;是當前全球數控加工程序驗證、機床模擬、工藝程序優化軟件領域的領導者。該軟件自1988年開始推向市場以來&#xf…

php數據庫創建文件失敗怎么回事,安裝zblogPHP提示“創建c_option.php失敗”解決方法...

有zblog用戶反應在安裝zblog的最后一步時提示“創建c_option.php失敗”&#xff0c;如下圖&#xff1a;本文來說明下這個問題的原因和解決辦法。問題產生的原因&#xff1a;c_option.php是zblog的數據庫配置文件&#xff0c;當安裝完成的時候程序會自動創建這個文件。如果你的主…

一次搞清楚Mysql聯合索引,以及聯合索引究竟用了多少

一群DBA朋友聊天&#xff0c;突然拋出一個某公司聯合索引的面試題&#xff0c;當時好多人都蒙了&#xff0c;這次針對這個問題&#xff0c;做了個簡單的實驗&#xff0c;把聯合索引的作用一次搞清楚 問題大概是這樣的&#xff0c;聯合索引&#xff08;a,b,c,d&#xff09;下面這…

CSS Variables

CSS原生變量(CSS自定義屬性) 示例地址&#xff1a;https://github.com/ccyinghua/Css-Variables 一、css原生變量的基礎用法 變量聲明使用兩根連詞線"--"表示變量&#xff0c;"$color"是屬于Sass的語法&#xff0c;"color"是屬于Less的語法&a…

【基礎中的基礎】引用類型和值類型,以及引用傳遞和值傳遞

一直在博客園懟人&#xff0c;非常慚愧。所以鄭重決定&#xff1a; 好好寫一篇干貨&#xff0c;然后再接著懟人。 這是一起幫上陳百萬同學的求助&#xff0c;講了一會之后&#xff0c;我覺得很有些普世價值&#xff0c;干脆就發到園子來。面向小白&#xff0c;高手輕拍。 我們從…

Java 7:使用NIO.2進行文件過濾–第3部分

大家好。 這是使用NIO.2系列進行文件過濾的第3部分。 對于那些尚未閱讀第1 部分或第2部分的人 &#xff0c;這里有個回顧。 NIO.2是自Java 7起JDK中包含的用于I / O操作的新API。使用此新API&#xff0c;您可以執行與java.io相同的操作&#xff0c;以及許多出色的功能&#xf…

python眾數問題給定含有n個元素的多重集合s_分治法求眾數 給定含有n個元素的多重集合S 聯合開發網 - pudn.com...

分治法求眾數所屬分類&#xff1a;數據結構開發工具&#xff1a;C/C文件大小&#xff1a;240KB下載次數&#xff1a;3上傳日期&#xff1a;2018-01-04 20:19:09上 傳 者&#xff1a;九鼎說明&#xff1a; 給定含有n個元素的多重集合S&#xff0c;每個元素在S中出現的次數稱為該…

mysql 5.0 亂碼,解決MySQL 5.0.16的亂碼問題

導讀&#xff1a;問&#xff1a;怎樣解決MySQL 5.0.16的亂碼問題?答&#xff1a;MySQL 5.0.16的亂碼問題可以用下面的方法解決&#xff1a;1.設置phpMyAdminLanguage:Chinese simplified (zh-utf-8)MySQL 字符集&#xff1a;UTF-8 Unicode (utf8)MySQL 連接校對 gbk_chinese_c…

Hadoop Serialization -- hadoop序列化具體解釋 (2)【Text,BytesWritable,NullWritable】

回想&#xff1a;回想序列化&#xff0c;事實上原書的結構非常清晰&#xff0c;我截圖給出書中的章節結構&#xff1a;序列化最基本的&#xff0c;最底層的是實現writable接口&#xff0c;wiritable規定讀和寫的游戲規則 &#xff08;void write(DataOutput out) throws IOExce…

我需要多少個線程?

這取決于您的應用程序。 但是對于那些希望對如何從生產站點購買的所有昂貴內核中擠出更多資金的人來說&#xff0c;請多多包涵&#xff0c;我將闡明圍繞多線程 Java應用程序的奧秘。 內容針對最典型的Java EE應用程序進行了“優化”&#xff0c;該應用程序具有Web前端&#xff…

H5網頁適配 iPhoneX,就是這么簡單

iPhoneX 取消了物理按鍵&#xff0c;改成底部小黑條&#xff0c;這一改動導致網頁出現了比較尷尬的屏幕適配問題。對于網頁而言&#xff0c;頂部&#xff08;劉海部位&#xff09;的適配問題瀏覽器已經做了處理&#xff0c;所以我們只需要關注底部與小黑條的適配問題即可&#…

python為什么closed_為什么python類的函數被調用兩次[關閉](Why a function of python class is called twice [closed])...

為什么python類的函數被調用兩次[關閉](Why a function of python class is called twice [closed])我遇到了兩次調用的python類函數的問題。 我正在使用Spyder IDE。這是我的簡單代碼class Test:def f(self):print("a")from Test import *t Test()t.f()當我按“運行…

php關聯數組和哈希表,php遍歷哈希表及關聯數組的實例代碼

有關php數組的分類&#xff0c;PHP數組分為&#xff1a;數字索引數組和關聯數組。其中數字索引數組和C語言中的數組一樣&#xff0c;下標是為0&#xff0c;1&#xff0c;2…而關聯數組下標可能是任意類型&#xff0c;與其它語言中的hash&#xff0c;map等結構相似。PHP遍歷關聯…

數字校園-云資源平臺 2014.10.26-人人通共享空間

近期,教育部在統計學校信息化建設情況,當中一項重要內容,作為三通兩平臺的一個環節,就是學校開通人人通空間的情況,網上普及了一下知識,不就是十多年前就玩的學校博客的變種嗎,網上有一些產品,也是沒有熱鬧起來,為要求而要求的多,既然要求,就來一個吧,花了幾天時間,也做了一個.…

VUE.js 中取得后臺原生HTML字符串 原樣顯示問題

今天使用vue調試頁面&#xff0c;發現了頁面上的一個問題&#xff0c;后臺數據傳過來的HTML字符串并沒有被轉換為正常的HTML代碼&#xff0c;一拍腦門&#xff0c;發現忘記轉換了&#xff0c;于是滿心歡喜加上了{{{}}}。但是之后構建發現報錯&#xff1a; 為此去官網上查了下…

高性能持久消息

總覽 盡管有許多可用于Java的高性能消息傳遞系統&#xff0c;但大多數都避免引用基準&#xff0c;包括持久消息傳遞和消息的序列化/反序列化。 這樣做有很多原因。 1&#xff09;您并不總是需要或想要持久消息2&#xff09;您希望使用自己的序列化選項。 避免使用它們的一個重要…

python去掉重復內容并按原來次序輸出元素_在Python中,從列表中刪除重復項以使所有元素在保留順序時都是唯一的最快的算法是什么?...

飲歌長嘯使用方法&#xff1a;lst [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 9, 3, 7, 7, 6, 6, 7, 5, 1…

Lucene –快速添加索引和搜索功能

什么是Lucene&#xff1f; Apache LuceneTM是完全用Java編寫的高性能&#xff0c;功能齊全的文本搜索引擎庫。 它是一項適用于幾乎所有需要全文搜索的應用程序的技術&#xff0c;尤其是跨平臺的應用程序。 Lucene可以純文本&#xff0c;整數&#xff0c;索引PDF&#xff0c;Of…

td 雙擊 編輯 php,雙擊表格td進行編輯

$(function(){//隔行換色// $("tbody tr:odd").css("background-color","#eee");var numId $(".tbody td");numId.dblclick(function(){var tdIns $(this);var tdpar $(this).parents("tr");//tdpar.remove();//current_…

前端開發之基礎知識-HTML(一)

1.1 html概述和基本結構 html概述 HTML是 HyperText Mark-up Language 的首字母簡寫&#xff0c;意思是超文本標記語言&#xff0c;超文本指的是超鏈接&#xff0c;標記指的是標簽&#xff0c;是一種用來制作網頁的語言&#xff0c;這種語言由一個個的標簽組成&#xff0c;用…