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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring中的@Cacheable开销

發布時間:2023/12/3 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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;} }

SpringInstrumentedCalculator和ManuallyInstrumentedCalculator與PlainCalculator完全相同,但是它們分別由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生成的子類,而aspectJCustom是ManuallyInstrumentedCalculator的實例,其中編織了我的自定義方面。

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

總結

以上是生活随笔為你收集整理的Spring中的@Cacheable开销的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。