le mie informazioni di contatto
Posta[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Nel mondo Java, la tecnologia di caching è ampiamente utilizzata per migliorare le prestazioni delle applicazioni e si divide principalmente in due categorie: caching remoto e caching locale. Il caching remoto, con le sue eccellenti prestazioni e flessibilità, viene spesso implementato attraverso soluzioni popolari come Redis e Memcached. La cache locale, con le sue caratteristiche di leggerezza e accesso veloce, è rappresentata da tecnologie come HashMap, Guava Cache, Caffeine ed Ehcache.
Approfondiremo i misteri del caching remoto nei futuri post del blog, ma oggi concentriamoci sul caching locale. Questo articolo ti guiderà innanzitutto attraverso la tecnologia di memorizzazione nella cache locale per fornirti una panoramica completa. Successivamente, approfondiremo la tecnologia di caching, conosciuta come la regina delle prestazioni, ed esploreremo i principi e i metodi di implementazione alla base. Infine, attraverso una serie di casi pratici, dimostreremo come utilizzare in modo efficace queste tecnologie di caching locale ad alte prestazioni nel lavoro quotidiano per migliorare l'efficienza dello sviluppo e le prestazioni delle applicazioni.
Utilizzando l'implementazione sottostante di Map, possiamo archiviare gli oggetti da memorizzare nella cache direttamente in memoria, il che è un metodo diretto ed efficiente.
Vantaggio: Questo metodo è semplice e diretto, senza fare affidamento su librerie esterne, ed è molto adatto per applicazioni con requisiti di caching semplici e scenari semplici.
Svantaggi: Tuttavia, questo metodo non dispone di un meccanismo di eliminazione automatica della cache. Se è necessario implementare strategie di memorizzazione nella cache più avanzate, potrebbero essere necessari costi di sviluppo personalizzati più elevati.
- public class LRUCache extends LinkedHashMap {
-
- /**
- * 可重入读写锁,保证并发读写安全性
- */
- private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- private Lock readLock = readWriteLock.readLock();
- private Lock writeLock = readWriteLock.writeLock();
-
- /**
- * 缓存大小限制
- */
- private int maxSize;
-
- public LRUCache(int maxSize) {
- super(maxSize + 1, 1.0f, true);
- this.maxSize = maxSize;
- }
-
- @Override
- public Object get(Object key) {
- readLock.lock();
- try {
- return super.get(key);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- public Object put(Object key, Object value) {
- writeLock.lock();
- try {
- return super.put(key, value);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest) {
- return this.size() > maxSize;
- }
- }
Guava Cache è una tecnologia di memorizzazione nella cache sviluppata da Google basata sull'algoritmo di sostituzione LRU (meno recentemente utilizzato). Tuttavia, con l'aumento della caffeina, la Guava Cache è gradualmente scomparsa dalla vista della gente. La caffeina non solo eredita i vantaggi della Guava Cache, ma la supera anche sotto molti aspetti. Sebbene qui non venga fornito un codice di esempio specifico, i lettori interessati a Guava Cache possono visitare il sito Web ufficiale per ulteriori informazioni.
Vantaggio: Guava Cache supporta l'impostazione di limiti di capacità massima e fornisce due strategie di scadenza: basate sul tempo di inserimento e sul tempo di accesso, e supporta anche alcune funzioni statistiche di base.
Svantaggi: Con il rilascio di Spring Boot 2 e Spring 5, Guava Cache non è più consigliato per entrambi.
Caffeine è una tecnologia di caching open source che utilizza l'algoritmo W-TinyLFU, ovvero una strategia di eliminazione della cache che combina i vantaggi di LRU e LFU (la minima frequenza utilizzata). Le prestazioni della cache di Caffeine sono vicine alla soluzione ottimale teorica e possono essere considerate una versione aggiornata di Guava Cache.
- public class CaffeineCacheTest {
-
- public static void main(String[] args) throws Exception {
- //创建guava cache
- Cache<String, String> loadingCache = Caffeine.newBuilder()
- //cache的初始容量
- .initialCapacity(5)
- //cache最大缓存数
- .maximumSize(10)
- //设置写缓存后n秒钟过期
- .expireAfterWrite(17, TimeUnit.SECONDS)
- //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
- //.expireAfterAccess(17, TimeUnit.SECONDS)
- .build();
- String key = "key";
- // 往缓存写数据
- loadingCache.put(key, "v");
-
- // 获取value的值,如果key不存在,获取value后再返回
- String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);
-
- // 删除key
- loadingCache.invalidate(key);
- }
-
- private static String getValueFromDB(String key) {
- return "v";
- }
- }
Ehcache è un framework di caching in-process Java puro noto per le sue prestazioni veloci ed efficienti. È ampiamente riconosciuto e funge da provider di cache predefinito per Hibernate.
Vantaggio : Ehcache fornisce un'ampia gamma di algoritmi di eliminazione della cache, inclusi LFU (usato meno frequentemente), LRU (usato meno recentemente) e FIFO (first in, first out). Supporta diversi tipi di archiviazione cache, come cache in-heap, cache off-heap e cache su disco, per adattarsi alle diverse esigenze di archiviazione. Inoltre, Ehcache supporta anche una varietà di soluzioni cluster, risolvendo efficacemente il problema della condivisione dei dati.
Svantaggi: Sebbene Ehcache eccelle sotto molti aspetti, è leggermente inferiore a Caffeine in termini di prestazioni.
-
- public class EncacheTest {
-
- public static void main(String[] args) throws Exception {
- // 声明一个cacheBuilder
- CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
- .withCache("encacheInstance", CacheConfigurationBuilder
- //声明一个容量为20的堆内缓存
- .newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
- .build(true);
- // 获取Cache实例
- Cache<String,String> myCache = cacheManager.getCache("encacheInstance", String.class, String.class);
- // 写缓存
- myCache.put("key","v");
- // 读缓存
- String value = myCache.get("key");
- // 移除换粗
- cacheManager.removeCache("myCache");
- cacheManager.close();
- }
- }
Cache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.build();
// 查找一个缓存元素, 没有查找到的时候返回null
Graph graph = cache.getIfPresent(key);
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.invalidate(key);
L'interfaccia Cache offre la possibilità di trovare, aggiornare e rimuovere gli elementi memorizzati nella cache utilizzando ricerche esplicite. Quando l'elemento memorizzato nella cache non può essere generato o viene generata un'eccezione durante il processo di generazione e la generazione dell'elemento fallisce, cache.get può restituire null .
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
Graph graph = cache.get(key);
// 批量查找缓存,如果缓存不存在则生成缓存元素
Map<Key, Graph> graphs = cache.getAll(keys);
Una LoadingCache è un'implementazione cache di una Cache a cui è stata aggiunta la funzionalità CacheLoader.
Se la cache non è presente, l'elemento cache corrispondente verrà generato tramite CacheLoader.load.
AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.buildAsync();
// 查找一个缓存元素, 没有查找到的时候返回null
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// 查找缓存元素,如果不存在,则异步生成
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.synchronous().invalidate(key);
AsyncCache è la forma asincrona di Cache, che fornisce la possibilità all'Executor di generare elementi della cache e restituire CompletableFuture. L'implementazione predefinita del pool di thread è ForkJoinPool.commonPool(), ma è anche possibile personalizzare la selezione del pool di thread sovrascrivendo e implementando il metodo Caffeine.executor(Executor).
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// 你可以选择: 去异步的封装一段同步操作来生成缓存元素
.buildAsync(key -> createExpensiveGraph(key));
// 你也可以选择: 构建一个异步缓存元素操作并返回一个future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// 查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Graph> graph = cache.get(key);
// 批量查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
AsyncLoadingCache è la forma asincrona di LoadingCache, che fornisce la funzione di caricamento asincrono per generare elementi di cache.
// 基于缓存内的元素个数进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> createExpensiveGraph(key));
// 基于缓存内元素权重进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumWeight(10_000)
.weigher((Key key, Graph graph) -> graph.vertices().size())
.build(key -> createExpensiveGraph(key));
// 基于固定的过期时间驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 基于不同的过期驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
// 当key和缓存元素都不再存在其他强引用的时候驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
// 当进行GC的时候进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
La strategia di aggiornamento può essere utilizzata solo in LoadingCache, a differenza dell'eliminazione, se l'elemento della cache viene interrogato durante l'aggiornamento, il suo vecchio valore verrà comunque restituito e il nuovo valore aggiornato non verrà restituito fino al completamento dell'aggiornamento dell'elemento.
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
La raccolta dei dati può essere attivata utilizzando il metodo Caffeine.recordStats(). Il metodo Cache.stats() restituirà un oggetto CacheStats, che conterrà alcuni indicatori statistici, come:
hitRate(): percentuale di riscontri della cache delle query
evictionCount(): numero di cache da eliminare
AverageLoadPenalty(): il tempo medio necessario per caricare i nuovi valori
Con il controller RESTful fornito da SpringBoot, puoi facilmente interrogare l'utilizzo della cache.